7064634aaa09939211ccdb5080118d9a0f71f728
[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      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
7939      * @param {String} which (first|prev|next|last|refresh)  which button to press.
7940      *
7941      */
7942     // private
7943     onClick : function(which){
7944         var ds = this.ds;
7945         switch(which){
7946             case "first":
7947                 ds.load({params:{start: 0, limit: this.pageSize}});
7948             break;
7949             case "prev":
7950                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7951             break;
7952             case "next":
7953                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7954             break;
7955             case "last":
7956                 var total = ds.getTotalCount();
7957                 var extra = total % this.pageSize;
7958                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7959                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7960             break;
7961             case "refresh":
7962                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7963             break;
7964         }
7965     },
7966
7967     /**
7968      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7969      * @param {Roo.data.Store} store The data store to unbind
7970      */
7971     unbind : function(ds){
7972         ds.un("beforeload", this.beforeLoad, this);
7973         ds.un("load", this.onLoad, this);
7974         ds.un("loadexception", this.onLoadError, this);
7975         ds.un("remove", this.updateInfo, this);
7976         ds.un("add", this.updateInfo, this);
7977         this.ds = undefined;
7978     },
7979
7980     /**
7981      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7982      * @param {Roo.data.Store} store The data store to bind
7983      */
7984     bind : function(ds){
7985         ds.on("beforeload", this.beforeLoad, this);
7986         ds.on("load", this.onLoad, this);
7987         ds.on("loadexception", this.onLoadError, this);
7988         ds.on("remove", this.updateInfo, this);
7989         ds.on("add", this.updateInfo, this);
7990         this.ds = ds;
7991     }
7992 });/*
7993  * Based on:
7994  * Ext JS Library 1.1.1
7995  * Copyright(c) 2006-2007, Ext JS, LLC.
7996  *
7997  * Originally Released Under LGPL - original licence link has changed is not relivant.
7998  *
7999  * Fork - LGPL
8000  * <script type="text/javascript">
8001  */
8002
8003 /**
8004  * @class Roo.Resizable
8005  * @extends Roo.util.Observable
8006  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8007  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8008  * 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
8009  * the element will be wrapped for you automatically.</p>
8010  * <p>Here is the list of valid resize handles:</p>
8011  * <pre>
8012 Value   Description
8013 ------  -------------------
8014  'n'     north
8015  's'     south
8016  'e'     east
8017  'w'     west
8018  'nw'    northwest
8019  'sw'    southwest
8020  'se'    southeast
8021  'ne'    northeast
8022  'hd'    horizontal drag
8023  'all'   all
8024 </pre>
8025  * <p>Here's an example showing the creation of a typical Resizable:</p>
8026  * <pre><code>
8027 var resizer = new Roo.Resizable("element-id", {
8028     handles: 'all',
8029     minWidth: 200,
8030     minHeight: 100,
8031     maxWidth: 500,
8032     maxHeight: 400,
8033     pinned: true
8034 });
8035 resizer.on("resize", myHandler);
8036 </code></pre>
8037  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8038  * resizer.east.setDisplayed(false);</p>
8039  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8040  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8041  * resize operation's new size (defaults to [0, 0])
8042  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8043  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8044  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8045  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8046  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8047  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8048  * @cfg {Number} width The width of the element in pixels (defaults to null)
8049  * @cfg {Number} height The height of the element in pixels (defaults to null)
8050  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8051  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8052  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8053  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8054  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8055  * in favor of the handles config option (defaults to false)
8056  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8057  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8058  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8059  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8060  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8061  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8062  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8063  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8064  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8065  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8066  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8067  * @constructor
8068  * Create a new resizable component
8069  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8070  * @param {Object} config configuration options
8071   */
8072 Roo.Resizable = function(el, config)
8073 {
8074     this.el = Roo.get(el);
8075
8076     if(config && config.wrap){
8077         config.resizeChild = this.el;
8078         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8079         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8080         this.el.setStyle("overflow", "hidden");
8081         this.el.setPositioning(config.resizeChild.getPositioning());
8082         config.resizeChild.clearPositioning();
8083         if(!config.width || !config.height){
8084             var csize = config.resizeChild.getSize();
8085             this.el.setSize(csize.width, csize.height);
8086         }
8087         if(config.pinned && !config.adjustments){
8088             config.adjustments = "auto";
8089         }
8090     }
8091
8092     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8093     this.proxy.unselectable();
8094     this.proxy.enableDisplayMode('block');
8095
8096     Roo.apply(this, config);
8097
8098     if(this.pinned){
8099         this.disableTrackOver = true;
8100         this.el.addClass("x-resizable-pinned");
8101     }
8102     // if the element isn't positioned, make it relative
8103     var position = this.el.getStyle("position");
8104     if(position != "absolute" && position != "fixed"){
8105         this.el.setStyle("position", "relative");
8106     }
8107     if(!this.handles){ // no handles passed, must be legacy style
8108         this.handles = 's,e,se';
8109         if(this.multiDirectional){
8110             this.handles += ',n,w';
8111         }
8112     }
8113     if(this.handles == "all"){
8114         this.handles = "n s e w ne nw se sw";
8115     }
8116     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8117     var ps = Roo.Resizable.positions;
8118     for(var i = 0, len = hs.length; i < len; i++){
8119         if(hs[i] && ps[hs[i]]){
8120             var pos = ps[hs[i]];
8121             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8122         }
8123     }
8124     // legacy
8125     this.corner = this.southeast;
8126     
8127     // updateBox = the box can move..
8128     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8129         this.updateBox = true;
8130     }
8131
8132     this.activeHandle = null;
8133
8134     if(this.resizeChild){
8135         if(typeof this.resizeChild == "boolean"){
8136             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8137         }else{
8138             this.resizeChild = Roo.get(this.resizeChild, true);
8139         }
8140     }
8141     
8142     if(this.adjustments == "auto"){
8143         var rc = this.resizeChild;
8144         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8145         if(rc && (hw || hn)){
8146             rc.position("relative");
8147             rc.setLeft(hw ? hw.el.getWidth() : 0);
8148             rc.setTop(hn ? hn.el.getHeight() : 0);
8149         }
8150         this.adjustments = [
8151             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8152             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8153         ];
8154     }
8155
8156     if(this.draggable){
8157         this.dd = this.dynamic ?
8158             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8159         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8160     }
8161
8162     // public events
8163     this.addEvents({
8164         /**
8165          * @event beforeresize
8166          * Fired before resize is allowed. Set enabled to false to cancel resize.
8167          * @param {Roo.Resizable} this
8168          * @param {Roo.EventObject} e The mousedown event
8169          */
8170         "beforeresize" : true,
8171         /**
8172          * @event resizing
8173          * Fired a resizing.
8174          * @param {Roo.Resizable} this
8175          * @param {Number} x The new x position
8176          * @param {Number} y The new y position
8177          * @param {Number} w The new w width
8178          * @param {Number} h The new h hight
8179          * @param {Roo.EventObject} e The mouseup event
8180          */
8181         "resizing" : true,
8182         /**
8183          * @event resize
8184          * Fired after a resize.
8185          * @param {Roo.Resizable} this
8186          * @param {Number} width The new width
8187          * @param {Number} height The new height
8188          * @param {Roo.EventObject} e The mouseup event
8189          */
8190         "resize" : true
8191     });
8192
8193     if(this.width !== null && this.height !== null){
8194         this.resizeTo(this.width, this.height);
8195     }else{
8196         this.updateChildSize();
8197     }
8198     if(Roo.isIE){
8199         this.el.dom.style.zoom = 1;
8200     }
8201     Roo.Resizable.superclass.constructor.call(this);
8202 };
8203
8204 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8205         resizeChild : false,
8206         adjustments : [0, 0],
8207         minWidth : 5,
8208         minHeight : 5,
8209         maxWidth : 10000,
8210         maxHeight : 10000,
8211         enabled : true,
8212         animate : false,
8213         duration : .35,
8214         dynamic : false,
8215         handles : false,
8216         multiDirectional : false,
8217         disableTrackOver : false,
8218         easing : 'easeOutStrong',
8219         widthIncrement : 0,
8220         heightIncrement : 0,
8221         pinned : false,
8222         width : null,
8223         height : null,
8224         preserveRatio : false,
8225         transparent: false,
8226         minX: 0,
8227         minY: 0,
8228         draggable: false,
8229
8230         /**
8231          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8232          */
8233         constrainTo: undefined,
8234         /**
8235          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8236          */
8237         resizeRegion: undefined,
8238
8239
8240     /**
8241      * Perform a manual resize
8242      * @param {Number} width
8243      * @param {Number} height
8244      */
8245     resizeTo : function(width, height){
8246         this.el.setSize(width, height);
8247         this.updateChildSize();
8248         this.fireEvent("resize", this, width, height, null);
8249     },
8250
8251     // private
8252     startSizing : function(e, handle){
8253         this.fireEvent("beforeresize", this, e);
8254         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8255
8256             if(!this.overlay){
8257                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8258                 this.overlay.unselectable();
8259                 this.overlay.enableDisplayMode("block");
8260                 this.overlay.on("mousemove", this.onMouseMove, this);
8261                 this.overlay.on("mouseup", this.onMouseUp, this);
8262             }
8263             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8264
8265             this.resizing = true;
8266             this.startBox = this.el.getBox();
8267             this.startPoint = e.getXY();
8268             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8269                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8270
8271             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8272             this.overlay.show();
8273
8274             if(this.constrainTo) {
8275                 var ct = Roo.get(this.constrainTo);
8276                 this.resizeRegion = ct.getRegion().adjust(
8277                     ct.getFrameWidth('t'),
8278                     ct.getFrameWidth('l'),
8279                     -ct.getFrameWidth('b'),
8280                     -ct.getFrameWidth('r')
8281                 );
8282             }
8283
8284             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8285             this.proxy.show();
8286             this.proxy.setBox(this.startBox);
8287             if(!this.dynamic){
8288                 this.proxy.setStyle('visibility', 'visible');
8289             }
8290         }
8291     },
8292
8293     // private
8294     onMouseDown : function(handle, e){
8295         if(this.enabled){
8296             e.stopEvent();
8297             this.activeHandle = handle;
8298             this.startSizing(e, handle);
8299         }
8300     },
8301
8302     // private
8303     onMouseUp : function(e){
8304         var size = this.resizeElement();
8305         this.resizing = false;
8306         this.handleOut();
8307         this.overlay.hide();
8308         this.proxy.hide();
8309         this.fireEvent("resize", this, size.width, size.height, e);
8310     },
8311
8312     // private
8313     updateChildSize : function(){
8314         
8315         if(this.resizeChild){
8316             var el = this.el;
8317             var child = this.resizeChild;
8318             var adj = this.adjustments;
8319             if(el.dom.offsetWidth){
8320                 var b = el.getSize(true);
8321                 child.setSize(b.width+adj[0], b.height+adj[1]);
8322             }
8323             // Second call here for IE
8324             // The first call enables instant resizing and
8325             // the second call corrects scroll bars if they
8326             // exist
8327             if(Roo.isIE){
8328                 setTimeout(function(){
8329                     if(el.dom.offsetWidth){
8330                         var b = el.getSize(true);
8331                         child.setSize(b.width+adj[0], b.height+adj[1]);
8332                     }
8333                 }, 10);
8334             }
8335         }
8336     },
8337
8338     // private
8339     snap : function(value, inc, min){
8340         if(!inc || !value) {
8341             return value;
8342         }
8343         var newValue = value;
8344         var m = value % inc;
8345         if(m > 0){
8346             if(m > (inc/2)){
8347                 newValue = value + (inc-m);
8348             }else{
8349                 newValue = value - m;
8350             }
8351         }
8352         return Math.max(min, newValue);
8353     },
8354
8355     // private
8356     resizeElement : function(){
8357         var box = this.proxy.getBox();
8358         if(this.updateBox){
8359             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8360         }else{
8361             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8362         }
8363         this.updateChildSize();
8364         if(!this.dynamic){
8365             this.proxy.hide();
8366         }
8367         return box;
8368     },
8369
8370     // private
8371     constrain : function(v, diff, m, mx){
8372         if(v - diff < m){
8373             diff = v - m;
8374         }else if(v - diff > mx){
8375             diff = mx - v;
8376         }
8377         return diff;
8378     },
8379
8380     // private
8381     onMouseMove : function(e){
8382         
8383         if(this.enabled){
8384             try{// try catch so if something goes wrong the user doesn't get hung
8385
8386             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8387                 return;
8388             }
8389
8390             //var curXY = this.startPoint;
8391             var curSize = this.curSize || this.startBox;
8392             var x = this.startBox.x, y = this.startBox.y;
8393             var ox = x, oy = y;
8394             var w = curSize.width, h = curSize.height;
8395             var ow = w, oh = h;
8396             var mw = this.minWidth, mh = this.minHeight;
8397             var mxw = this.maxWidth, mxh = this.maxHeight;
8398             var wi = this.widthIncrement;
8399             var hi = this.heightIncrement;
8400
8401             var eventXY = e.getXY();
8402             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8403             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8404
8405             var pos = this.activeHandle.position;
8406
8407             switch(pos){
8408                 case "east":
8409                     w += diffX;
8410                     w = Math.min(Math.max(mw, w), mxw);
8411                     break;
8412              
8413                 case "south":
8414                     h += diffY;
8415                     h = Math.min(Math.max(mh, h), mxh);
8416                     break;
8417                 case "southeast":
8418                     w += diffX;
8419                     h += diffY;
8420                     w = Math.min(Math.max(mw, w), mxw);
8421                     h = Math.min(Math.max(mh, h), mxh);
8422                     break;
8423                 case "north":
8424                     diffY = this.constrain(h, diffY, mh, mxh);
8425                     y += diffY;
8426                     h -= diffY;
8427                     break;
8428                 case "hdrag":
8429                     
8430                     if (wi) {
8431                         var adiffX = Math.abs(diffX);
8432                         var sub = (adiffX % wi); // how much 
8433                         if (sub > (wi/2)) { // far enough to snap
8434                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8435                         } else {
8436                             // remove difference.. 
8437                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8438                         }
8439                     }
8440                     x += diffX;
8441                     x = Math.max(this.minX, x);
8442                     break;
8443                 case "west":
8444                     diffX = this.constrain(w, diffX, mw, mxw);
8445                     x += diffX;
8446                     w -= diffX;
8447                     break;
8448                 case "northeast":
8449                     w += diffX;
8450                     w = Math.min(Math.max(mw, w), mxw);
8451                     diffY = this.constrain(h, diffY, mh, mxh);
8452                     y += diffY;
8453                     h -= diffY;
8454                     break;
8455                 case "northwest":
8456                     diffX = this.constrain(w, diffX, mw, mxw);
8457                     diffY = this.constrain(h, diffY, mh, mxh);
8458                     y += diffY;
8459                     h -= diffY;
8460                     x += diffX;
8461                     w -= diffX;
8462                     break;
8463                case "southwest":
8464                     diffX = this.constrain(w, diffX, mw, mxw);
8465                     h += diffY;
8466                     h = Math.min(Math.max(mh, h), mxh);
8467                     x += diffX;
8468                     w -= diffX;
8469                     break;
8470             }
8471
8472             var sw = this.snap(w, wi, mw);
8473             var sh = this.snap(h, hi, mh);
8474             if(sw != w || sh != h){
8475                 switch(pos){
8476                     case "northeast":
8477                         y -= sh - h;
8478                     break;
8479                     case "north":
8480                         y -= sh - h;
8481                         break;
8482                     case "southwest":
8483                         x -= sw - w;
8484                     break;
8485                     case "west":
8486                         x -= sw - w;
8487                         break;
8488                     case "northwest":
8489                         x -= sw - w;
8490                         y -= sh - h;
8491                     break;
8492                 }
8493                 w = sw;
8494                 h = sh;
8495             }
8496
8497             if(this.preserveRatio){
8498                 switch(pos){
8499                     case "southeast":
8500                     case "east":
8501                         h = oh * (w/ow);
8502                         h = Math.min(Math.max(mh, h), mxh);
8503                         w = ow * (h/oh);
8504                        break;
8505                     case "south":
8506                         w = ow * (h/oh);
8507                         w = Math.min(Math.max(mw, w), mxw);
8508                         h = oh * (w/ow);
8509                         break;
8510                     case "northeast":
8511                         w = ow * (h/oh);
8512                         w = Math.min(Math.max(mw, w), mxw);
8513                         h = oh * (w/ow);
8514                     break;
8515                     case "north":
8516                         var tw = w;
8517                         w = ow * (h/oh);
8518                         w = Math.min(Math.max(mw, w), mxw);
8519                         h = oh * (w/ow);
8520                         x += (tw - w) / 2;
8521                         break;
8522                     case "southwest":
8523                         h = oh * (w/ow);
8524                         h = Math.min(Math.max(mh, h), mxh);
8525                         var tw = w;
8526                         w = ow * (h/oh);
8527                         x += tw - w;
8528                         break;
8529                     case "west":
8530                         var th = h;
8531                         h = oh * (w/ow);
8532                         h = Math.min(Math.max(mh, h), mxh);
8533                         y += (th - h) / 2;
8534                         var tw = w;
8535                         w = ow * (h/oh);
8536                         x += tw - w;
8537                        break;
8538                     case "northwest":
8539                         var tw = w;
8540                         var th = h;
8541                         h = oh * (w/ow);
8542                         h = Math.min(Math.max(mh, h), mxh);
8543                         w = ow * (h/oh);
8544                         y += th - h;
8545                         x += tw - w;
8546                        break;
8547
8548                 }
8549             }
8550             if (pos == 'hdrag') {
8551                 w = ow;
8552             }
8553             this.proxy.setBounds(x, y, w, h);
8554             if(this.dynamic){
8555                 this.resizeElement();
8556             }
8557             }catch(e){}
8558         }
8559         this.fireEvent("resizing", this, x, y, w, h, e);
8560     },
8561
8562     // private
8563     handleOver : function(){
8564         if(this.enabled){
8565             this.el.addClass("x-resizable-over");
8566         }
8567     },
8568
8569     // private
8570     handleOut : function(){
8571         if(!this.resizing){
8572             this.el.removeClass("x-resizable-over");
8573         }
8574     },
8575
8576     /**
8577      * Returns the element this component is bound to.
8578      * @return {Roo.Element}
8579      */
8580     getEl : function(){
8581         return this.el;
8582     },
8583
8584     /**
8585      * Returns the resizeChild element (or null).
8586      * @return {Roo.Element}
8587      */
8588     getResizeChild : function(){
8589         return this.resizeChild;
8590     },
8591     groupHandler : function()
8592     {
8593         
8594     },
8595     /**
8596      * Destroys this resizable. If the element was wrapped and
8597      * removeEl is not true then the element remains.
8598      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8599      */
8600     destroy : function(removeEl){
8601         this.proxy.remove();
8602         if(this.overlay){
8603             this.overlay.removeAllListeners();
8604             this.overlay.remove();
8605         }
8606         var ps = Roo.Resizable.positions;
8607         for(var k in ps){
8608             if(typeof ps[k] != "function" && this[ps[k]]){
8609                 var h = this[ps[k]];
8610                 h.el.removeAllListeners();
8611                 h.el.remove();
8612             }
8613         }
8614         if(removeEl){
8615             this.el.update("");
8616             this.el.remove();
8617         }
8618     }
8619 });
8620
8621 // private
8622 // hash to map config positions to true positions
8623 Roo.Resizable.positions = {
8624     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8625     hd: "hdrag"
8626 };
8627
8628 // private
8629 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8630     if(!this.tpl){
8631         // only initialize the template if resizable is used
8632         var tpl = Roo.DomHelper.createTemplate(
8633             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8634         );
8635         tpl.compile();
8636         Roo.Resizable.Handle.prototype.tpl = tpl;
8637     }
8638     this.position = pos;
8639     this.rz = rz;
8640     // show north drag fro topdra
8641     var handlepos = pos == 'hdrag' ? 'north' : pos;
8642     
8643     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8644     if (pos == 'hdrag') {
8645         this.el.setStyle('cursor', 'pointer');
8646     }
8647     this.el.unselectable();
8648     if(transparent){
8649         this.el.setOpacity(0);
8650     }
8651     this.el.on("mousedown", this.onMouseDown, this);
8652     if(!disableTrackOver){
8653         this.el.on("mouseover", this.onMouseOver, this);
8654         this.el.on("mouseout", this.onMouseOut, this);
8655     }
8656 };
8657
8658 // private
8659 Roo.Resizable.Handle.prototype = {
8660     afterResize : function(rz){
8661         Roo.log('after?');
8662         // do nothing
8663     },
8664     // private
8665     onMouseDown : function(e){
8666         this.rz.onMouseDown(this, e);
8667     },
8668     // private
8669     onMouseOver : function(e){
8670         this.rz.handleOver(this, e);
8671     },
8672     // private
8673     onMouseOut : function(e){
8674         this.rz.handleOut(this, e);
8675     }
8676 };/*
8677  * Based on:
8678  * Ext JS Library 1.1.1
8679  * Copyright(c) 2006-2007, Ext JS, LLC.
8680  *
8681  * Originally Released Under LGPL - original licence link has changed is not relivant.
8682  *
8683  * Fork - LGPL
8684  * <script type="text/javascript">
8685  */
8686
8687 /**
8688  * @class Roo.Editor
8689  * @extends Roo.Component
8690  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8691  * @constructor
8692  * Create a new Editor
8693  * @param {Roo.form.Field} field The Field object (or descendant)
8694  * @param {Object} config The config object
8695  */
8696 Roo.Editor = function(field, config){
8697     Roo.Editor.superclass.constructor.call(this, config);
8698     this.field = field;
8699     this.addEvents({
8700         /**
8701              * @event beforestartedit
8702              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8703              * false from the handler of this event.
8704              * @param {Editor} this
8705              * @param {Roo.Element} boundEl The underlying element bound to this editor
8706              * @param {Mixed} value The field value being set
8707              */
8708         "beforestartedit" : true,
8709         /**
8710              * @event startedit
8711              * Fires when this editor is displayed
8712              * @param {Roo.Element} boundEl The underlying element bound to this editor
8713              * @param {Mixed} value The starting field value
8714              */
8715         "startedit" : true,
8716         /**
8717              * @event beforecomplete
8718              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8719              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8720              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8721              * event will not fire since no edit actually occurred.
8722              * @param {Editor} this
8723              * @param {Mixed} value The current field value
8724              * @param {Mixed} startValue The original field value
8725              */
8726         "beforecomplete" : true,
8727         /**
8728              * @event complete
8729              * Fires after editing is complete and any changed value has been written to the underlying field.
8730              * @param {Editor} this
8731              * @param {Mixed} value The current field value
8732              * @param {Mixed} startValue The original field value
8733              */
8734         "complete" : true,
8735         /**
8736          * @event specialkey
8737          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8738          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8739          * @param {Roo.form.Field} this
8740          * @param {Roo.EventObject} e The event object
8741          */
8742         "specialkey" : true
8743     });
8744 };
8745
8746 Roo.extend(Roo.Editor, Roo.Component, {
8747     /**
8748      * @cfg {Boolean/String} autosize
8749      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8750      * or "height" to adopt the height only (defaults to false)
8751      */
8752     /**
8753      * @cfg {Boolean} revertInvalid
8754      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8755      * validation fails (defaults to true)
8756      */
8757     /**
8758      * @cfg {Boolean} ignoreNoChange
8759      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8760      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8761      * will never be ignored.
8762      */
8763     /**
8764      * @cfg {Boolean} hideEl
8765      * False to keep the bound element visible while the editor is displayed (defaults to true)
8766      */
8767     /**
8768      * @cfg {Mixed} value
8769      * The data value of the underlying field (defaults to "")
8770      */
8771     value : "",
8772     /**
8773      * @cfg {String} alignment
8774      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8775      */
8776     alignment: "c-c?",
8777     /**
8778      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8779      * for bottom-right shadow (defaults to "frame")
8780      */
8781     shadow : "frame",
8782     /**
8783      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8784      */
8785     constrain : false,
8786     /**
8787      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8788      */
8789     completeOnEnter : false,
8790     /**
8791      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8792      */
8793     cancelOnEsc : false,
8794     /**
8795      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8796      */
8797     updateEl : false,
8798
8799     // private
8800     onRender : function(ct, position){
8801         this.el = new Roo.Layer({
8802             shadow: this.shadow,
8803             cls: "x-editor",
8804             parentEl : ct,
8805             shim : this.shim,
8806             shadowOffset:4,
8807             id: this.id,
8808             constrain: this.constrain
8809         });
8810         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8811         if(this.field.msgTarget != 'title'){
8812             this.field.msgTarget = 'qtip';
8813         }
8814         this.field.render(this.el);
8815         if(Roo.isGecko){
8816             this.field.el.dom.setAttribute('autocomplete', 'off');
8817         }
8818         this.field.on("specialkey", this.onSpecialKey, this);
8819         if(this.swallowKeys){
8820             this.field.el.swallowEvent(['keydown','keypress']);
8821         }
8822         this.field.show();
8823         this.field.on("blur", this.onBlur, this);
8824         if(this.field.grow){
8825             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8826         }
8827     },
8828
8829     onSpecialKey : function(field, e)
8830     {
8831         //Roo.log('editor onSpecialKey');
8832         if(this.completeOnEnter && e.getKey() == e.ENTER){
8833             e.stopEvent();
8834             this.completeEdit();
8835             return;
8836         }
8837         // do not fire special key otherwise it might hide close the editor...
8838         if(e.getKey() == e.ENTER){    
8839             return;
8840         }
8841         if(this.cancelOnEsc && e.getKey() == e.ESC){
8842             this.cancelEdit();
8843             return;
8844         } 
8845         this.fireEvent('specialkey', field, e);
8846     
8847     },
8848
8849     /**
8850      * Starts the editing process and shows the editor.
8851      * @param {String/HTMLElement/Element} el The element to edit
8852      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8853       * to the innerHTML of el.
8854      */
8855     startEdit : function(el, value){
8856         if(this.editing){
8857             this.completeEdit();
8858         }
8859         this.boundEl = Roo.get(el);
8860         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8861         if(!this.rendered){
8862             this.render(this.parentEl || document.body);
8863         }
8864         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8865             return;
8866         }
8867         this.startValue = v;
8868         this.field.setValue(v);
8869         if(this.autoSize){
8870             var sz = this.boundEl.getSize();
8871             switch(this.autoSize){
8872                 case "width":
8873                 this.setSize(sz.width,  "");
8874                 break;
8875                 case "height":
8876                 this.setSize("",  sz.height);
8877                 break;
8878                 default:
8879                 this.setSize(sz.width,  sz.height);
8880             }
8881         }
8882         this.el.alignTo(this.boundEl, this.alignment);
8883         this.editing = true;
8884         if(Roo.QuickTips){
8885             Roo.QuickTips.disable();
8886         }
8887         this.show();
8888     },
8889
8890     /**
8891      * Sets the height and width of this editor.
8892      * @param {Number} width The new width
8893      * @param {Number} height The new height
8894      */
8895     setSize : function(w, h){
8896         this.field.setSize(w, h);
8897         if(this.el){
8898             this.el.sync();
8899         }
8900     },
8901
8902     /**
8903      * Realigns the editor to the bound field based on the current alignment config value.
8904      */
8905     realign : function(){
8906         this.el.alignTo(this.boundEl, this.alignment);
8907     },
8908
8909     /**
8910      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8911      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8912      */
8913     completeEdit : function(remainVisible){
8914         if(!this.editing){
8915             return;
8916         }
8917         var v = this.getValue();
8918         if(this.revertInvalid !== false && !this.field.isValid()){
8919             v = this.startValue;
8920             this.cancelEdit(true);
8921         }
8922         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8923             this.editing = false;
8924             this.hide();
8925             return;
8926         }
8927         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8928             this.editing = false;
8929             if(this.updateEl && this.boundEl){
8930                 this.boundEl.update(v);
8931             }
8932             if(remainVisible !== true){
8933                 this.hide();
8934             }
8935             this.fireEvent("complete", this, v, this.startValue);
8936         }
8937     },
8938
8939     // private
8940     onShow : function(){
8941         this.el.show();
8942         if(this.hideEl !== false){
8943             this.boundEl.hide();
8944         }
8945         this.field.show();
8946         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8947             this.fixIEFocus = true;
8948             this.deferredFocus.defer(50, this);
8949         }else{
8950             this.field.focus();
8951         }
8952         this.fireEvent("startedit", this.boundEl, this.startValue);
8953     },
8954
8955     deferredFocus : function(){
8956         if(this.editing){
8957             this.field.focus();
8958         }
8959     },
8960
8961     /**
8962      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8963      * reverted to the original starting value.
8964      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8965      * cancel (defaults to false)
8966      */
8967     cancelEdit : function(remainVisible){
8968         if(this.editing){
8969             this.setValue(this.startValue);
8970             if(remainVisible !== true){
8971                 this.hide();
8972             }
8973         }
8974     },
8975
8976     // private
8977     onBlur : function(){
8978         if(this.allowBlur !== true && this.editing){
8979             this.completeEdit();
8980         }
8981     },
8982
8983     // private
8984     onHide : function(){
8985         if(this.editing){
8986             this.completeEdit();
8987             return;
8988         }
8989         this.field.blur();
8990         if(this.field.collapse){
8991             this.field.collapse();
8992         }
8993         this.el.hide();
8994         if(this.hideEl !== false){
8995             this.boundEl.show();
8996         }
8997         if(Roo.QuickTips){
8998             Roo.QuickTips.enable();
8999         }
9000     },
9001
9002     /**
9003      * Sets the data value of the editor
9004      * @param {Mixed} value Any valid value supported by the underlying field
9005      */
9006     setValue : function(v){
9007         this.field.setValue(v);
9008     },
9009
9010     /**
9011      * Gets the data value of the editor
9012      * @return {Mixed} The data value
9013      */
9014     getValue : function(){
9015         return this.field.getValue();
9016     }
9017 });/*
9018  * Based on:
9019  * Ext JS Library 1.1.1
9020  * Copyright(c) 2006-2007, Ext JS, LLC.
9021  *
9022  * Originally Released Under LGPL - original licence link has changed is not relivant.
9023  *
9024  * Fork - LGPL
9025  * <script type="text/javascript">
9026  */
9027  
9028 /**
9029  * @class Roo.BasicDialog
9030  * @extends Roo.util.Observable
9031  * @parent none builder
9032  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9033  * <pre><code>
9034 var dlg = new Roo.BasicDialog("my-dlg", {
9035     height: 200,
9036     width: 300,
9037     minHeight: 100,
9038     minWidth: 150,
9039     modal: true,
9040     proxyDrag: true,
9041     shadow: true
9042 });
9043 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9044 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9045 dlg.addButton('Cancel', dlg.hide, dlg);
9046 dlg.show();
9047 </code></pre>
9048   <b>A Dialog should always be a direct child of the body element.</b>
9049  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9050  * @cfg {String} title Default text to display in the title bar (defaults to null)
9051  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9052  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9053  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9054  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9055  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9056  * (defaults to null with no animation)
9057  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9058  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9059  * property for valid values (defaults to 'all')
9060  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9061  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9062  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9063  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9064  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9065  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9066  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9067  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9068  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9069  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9070  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9071  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9072  * draggable = true (defaults to false)
9073  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9074  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9075  * shadow (defaults to false)
9076  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9077  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9078  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9079  * @cfg {Array} buttons Array of buttons
9080  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9081  * @constructor
9082  * Create a new BasicDialog.
9083  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9084  * @param {Object} config Configuration options
9085  */
9086 Roo.BasicDialog = function(el, config){
9087     this.el = Roo.get(el);
9088     var dh = Roo.DomHelper;
9089     if(!this.el && config && config.autoCreate){
9090         if(typeof config.autoCreate == "object"){
9091             if(!config.autoCreate.id){
9092                 config.autoCreate.id = el;
9093             }
9094             this.el = dh.append(document.body,
9095                         config.autoCreate, true);
9096         }else{
9097             this.el = dh.append(document.body,
9098                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9099         }
9100     }
9101     el = this.el;
9102     el.setDisplayed(true);
9103     el.hide = this.hideAction;
9104     this.id = el.id;
9105     el.addClass("x-dlg");
9106
9107     Roo.apply(this, config);
9108
9109     this.proxy = el.createProxy("x-dlg-proxy");
9110     this.proxy.hide = this.hideAction;
9111     this.proxy.setOpacity(.5);
9112     this.proxy.hide();
9113
9114     if(config.width){
9115         el.setWidth(config.width);
9116     }
9117     if(config.height){
9118         el.setHeight(config.height);
9119     }
9120     this.size = el.getSize();
9121     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9122         this.xy = [config.x,config.y];
9123     }else{
9124         this.xy = el.getCenterXY(true);
9125     }
9126     /** The header element @type Roo.Element */
9127     this.header = el.child("> .x-dlg-hd");
9128     /** The body element @type Roo.Element */
9129     this.body = el.child("> .x-dlg-bd");
9130     /** The footer element @type Roo.Element */
9131     this.footer = el.child("> .x-dlg-ft");
9132
9133     if(!this.header){
9134         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9135     }
9136     if(!this.body){
9137         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9138     }
9139
9140     this.header.unselectable();
9141     if(this.title){
9142         this.header.update(this.title);
9143     }
9144     // this element allows the dialog to be focused for keyboard event
9145     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9146     this.focusEl.swallowEvent("click", true);
9147
9148     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9149
9150     // wrap the body and footer for special rendering
9151     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9152     if(this.footer){
9153         this.bwrap.dom.appendChild(this.footer.dom);
9154     }
9155
9156     this.bg = this.el.createChild({
9157         tag: "div", cls:"x-dlg-bg",
9158         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9159     });
9160     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9161
9162
9163     if(this.autoScroll !== false && !this.autoTabs){
9164         this.body.setStyle("overflow", "auto");
9165     }
9166
9167     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9168
9169     if(this.closable !== false){
9170         this.el.addClass("x-dlg-closable");
9171         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9172         this.close.on("click", this.closeClick, this);
9173         this.close.addClassOnOver("x-dlg-close-over");
9174     }
9175     if(this.collapsible !== false){
9176         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9177         this.collapseBtn.on("click", this.collapseClick, this);
9178         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9179         this.header.on("dblclick", this.collapseClick, this);
9180     }
9181     if(this.resizable !== false){
9182         this.el.addClass("x-dlg-resizable");
9183         this.resizer = new Roo.Resizable(el, {
9184             minWidth: this.minWidth || 80,
9185             minHeight:this.minHeight || 80,
9186             handles: this.resizeHandles || "all",
9187             pinned: true
9188         });
9189         this.resizer.on("beforeresize", this.beforeResize, this);
9190         this.resizer.on("resize", this.onResize, this);
9191     }
9192     if(this.draggable !== false){
9193         el.addClass("x-dlg-draggable");
9194         if (!this.proxyDrag) {
9195             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9196         }
9197         else {
9198             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9199         }
9200         dd.setHandleElId(this.header.id);
9201         dd.endDrag = this.endMove.createDelegate(this);
9202         dd.startDrag = this.startMove.createDelegate(this);
9203         dd.onDrag = this.onDrag.createDelegate(this);
9204         dd.scroll = false;
9205         this.dd = dd;
9206     }
9207     if(this.modal){
9208         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9209         this.mask.enableDisplayMode("block");
9210         this.mask.hide();
9211         this.el.addClass("x-dlg-modal");
9212     }
9213     if(this.shadow){
9214         this.shadow = new Roo.Shadow({
9215             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9216             offset : this.shadowOffset
9217         });
9218     }else{
9219         this.shadowOffset = 0;
9220     }
9221     if(Roo.useShims && this.shim !== false){
9222         this.shim = this.el.createShim();
9223         this.shim.hide = this.hideAction;
9224         this.shim.hide();
9225     }else{
9226         this.shim = false;
9227     }
9228     if(this.autoTabs){
9229         this.initTabs();
9230     }
9231     if (this.buttons) { 
9232         var bts= this.buttons;
9233         this.buttons = [];
9234         Roo.each(bts, function(b) {
9235             this.addButton(b);
9236         }, this);
9237     }
9238     
9239     
9240     this.addEvents({
9241         /**
9242          * @event keydown
9243          * Fires when a key is pressed
9244          * @param {Roo.BasicDialog} this
9245          * @param {Roo.EventObject} e
9246          */
9247         "keydown" : true,
9248         /**
9249          * @event move
9250          * Fires when this dialog is moved by the user.
9251          * @param {Roo.BasicDialog} this
9252          * @param {Number} x The new page X
9253          * @param {Number} y The new page Y
9254          */
9255         "move" : true,
9256         /**
9257          * @event resize
9258          * Fires when this dialog is resized by the user.
9259          * @param {Roo.BasicDialog} this
9260          * @param {Number} width The new width
9261          * @param {Number} height The new height
9262          */
9263         "resize" : true,
9264         /**
9265          * @event beforehide
9266          * Fires before this dialog is hidden.
9267          * @param {Roo.BasicDialog} this
9268          */
9269         "beforehide" : true,
9270         /**
9271          * @event hide
9272          * Fires when this dialog is hidden.
9273          * @param {Roo.BasicDialog} this
9274          */
9275         "hide" : true,
9276         /**
9277          * @event beforeshow
9278          * Fires before this dialog is shown.
9279          * @param {Roo.BasicDialog} this
9280          */
9281         "beforeshow" : true,
9282         /**
9283          * @event show
9284          * Fires when this dialog is shown.
9285          * @param {Roo.BasicDialog} this
9286          */
9287         "show" : true
9288     });
9289     el.on("keydown", this.onKeyDown, this);
9290     el.on("mousedown", this.toFront, this);
9291     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9292     this.el.hide();
9293     Roo.DialogManager.register(this);
9294     Roo.BasicDialog.superclass.constructor.call(this);
9295 };
9296
9297 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9298     shadowOffset: Roo.isIE ? 6 : 5,
9299     minHeight: 80,
9300     minWidth: 200,
9301     minButtonWidth: 75,
9302     defaultButton: null,
9303     buttonAlign: "right",
9304     tabTag: 'div',
9305     firstShow: true,
9306
9307     /**
9308      * Sets the dialog title text
9309      * @param {String} text The title text to display
9310      * @return {Roo.BasicDialog} this
9311      */
9312     setTitle : function(text){
9313         this.header.update(text);
9314         return this;
9315     },
9316
9317     // private
9318     closeClick : function(){
9319         this.hide();
9320     },
9321
9322     // private
9323     collapseClick : function(){
9324         this[this.collapsed ? "expand" : "collapse"]();
9325     },
9326
9327     /**
9328      * Collapses the dialog to its minimized state (only the title bar is visible).
9329      * Equivalent to the user clicking the collapse dialog button.
9330      */
9331     collapse : function(){
9332         if(!this.collapsed){
9333             this.collapsed = true;
9334             this.el.addClass("x-dlg-collapsed");
9335             this.restoreHeight = this.el.getHeight();
9336             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9337         }
9338     },
9339
9340     /**
9341      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9342      * clicking the expand dialog button.
9343      */
9344     expand : function(){
9345         if(this.collapsed){
9346             this.collapsed = false;
9347             this.el.removeClass("x-dlg-collapsed");
9348             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9349         }
9350     },
9351
9352     /**
9353      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9354      * @return {Roo.TabPanel} The tabs component
9355      */
9356     initTabs : function(){
9357         var tabs = this.getTabs();
9358         while(tabs.getTab(0)){
9359             tabs.removeTab(0);
9360         }
9361         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9362             var dom = el.dom;
9363             tabs.addTab(Roo.id(dom), dom.title);
9364             dom.title = "";
9365         });
9366         tabs.activate(0);
9367         return tabs;
9368     },
9369
9370     // private
9371     beforeResize : function(){
9372         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9373     },
9374
9375     // private
9376     onResize : function(){
9377         this.refreshSize();
9378         this.syncBodyHeight();
9379         this.adjustAssets();
9380         this.focus();
9381         this.fireEvent("resize", this, this.size.width, this.size.height);
9382     },
9383
9384     // private
9385     onKeyDown : function(e){
9386         if(this.isVisible()){
9387             this.fireEvent("keydown", this, e);
9388         }
9389     },
9390
9391     /**
9392      * Resizes the dialog.
9393      * @param {Number} width
9394      * @param {Number} height
9395      * @return {Roo.BasicDialog} this
9396      */
9397     resizeTo : function(width, height){
9398         this.el.setSize(width, height);
9399         this.size = {width: width, height: height};
9400         this.syncBodyHeight();
9401         if(this.fixedcenter){
9402             this.center();
9403         }
9404         if(this.isVisible()){
9405             this.constrainXY();
9406             this.adjustAssets();
9407         }
9408         this.fireEvent("resize", this, width, height);
9409         return this;
9410     },
9411
9412
9413     /**
9414      * Resizes the dialog to fit the specified content size.
9415      * @param {Number} width
9416      * @param {Number} height
9417      * @return {Roo.BasicDialog} this
9418      */
9419     setContentSize : function(w, h){
9420         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9421         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9422         //if(!this.el.isBorderBox()){
9423             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9424             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9425         //}
9426         if(this.tabs){
9427             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9428             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9429         }
9430         this.resizeTo(w, h);
9431         return this;
9432     },
9433
9434     /**
9435      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9436      * executed in response to a particular key being pressed while the dialog is active.
9437      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9438      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9439      * @param {Function} fn The function to call
9440      * @param {Object} scope (optional) The scope of the function
9441      * @return {Roo.BasicDialog} this
9442      */
9443     addKeyListener : function(key, fn, scope){
9444         var keyCode, shift, ctrl, alt;
9445         if(typeof key == "object" && !(key instanceof Array)){
9446             keyCode = key["key"];
9447             shift = key["shift"];
9448             ctrl = key["ctrl"];
9449             alt = key["alt"];
9450         }else{
9451             keyCode = key;
9452         }
9453         var handler = function(dlg, e){
9454             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9455                 var k = e.getKey();
9456                 if(keyCode instanceof Array){
9457                     for(var i = 0, len = keyCode.length; i < len; i++){
9458                         if(keyCode[i] == k){
9459                           fn.call(scope || window, dlg, k, e);
9460                           return;
9461                         }
9462                     }
9463                 }else{
9464                     if(k == keyCode){
9465                         fn.call(scope || window, dlg, k, e);
9466                     }
9467                 }
9468             }
9469         };
9470         this.on("keydown", handler);
9471         return this;
9472     },
9473
9474     /**
9475      * Returns the TabPanel component (creates it if it doesn't exist).
9476      * Note: If you wish to simply check for the existence of tabs without creating them,
9477      * check for a null 'tabs' property.
9478      * @return {Roo.TabPanel} The tabs component
9479      */
9480     getTabs : function(){
9481         if(!this.tabs){
9482             this.el.addClass("x-dlg-auto-tabs");
9483             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9484             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9485         }
9486         return this.tabs;
9487     },
9488
9489     /**
9490      * Adds a button to the footer section of the dialog.
9491      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9492      * object or a valid Roo.DomHelper element config
9493      * @param {Function} handler The function called when the button is clicked
9494      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9495      * @return {Roo.Button} The new button
9496      */
9497     addButton : function(config, handler, scope){
9498         var dh = Roo.DomHelper;
9499         if(!this.footer){
9500             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9501         }
9502         if(!this.btnContainer){
9503             var tb = this.footer.createChild({
9504
9505                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9506                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9507             }, null, true);
9508             this.btnContainer = tb.firstChild.firstChild.firstChild;
9509         }
9510         var bconfig = {
9511             handler: handler,
9512             scope: scope,
9513             minWidth: this.minButtonWidth,
9514             hideParent:true
9515         };
9516         if(typeof config == "string"){
9517             bconfig.text = config;
9518         }else{
9519             if(config.tag){
9520                 bconfig.dhconfig = config;
9521             }else{
9522                 Roo.apply(bconfig, config);
9523             }
9524         }
9525         var fc = false;
9526         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9527             bconfig.position = Math.max(0, bconfig.position);
9528             fc = this.btnContainer.childNodes[bconfig.position];
9529         }
9530          
9531         var btn = new Roo.Button(
9532             fc ? 
9533                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9534                 : this.btnContainer.appendChild(document.createElement("td")),
9535             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9536             bconfig
9537         );
9538         this.syncBodyHeight();
9539         if(!this.buttons){
9540             /**
9541              * Array of all the buttons that have been added to this dialog via addButton
9542              * @type Array
9543              */
9544             this.buttons = [];
9545         }
9546         this.buttons.push(btn);
9547         return btn;
9548     },
9549
9550     /**
9551      * Sets the default button to be focused when the dialog is displayed.
9552      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9553      * @return {Roo.BasicDialog} this
9554      */
9555     setDefaultButton : function(btn){
9556         this.defaultButton = btn;
9557         return this;
9558     },
9559
9560     // private
9561     getHeaderFooterHeight : function(safe){
9562         var height = 0;
9563         if(this.header){
9564            height += this.header.getHeight();
9565         }
9566         if(this.footer){
9567            var fm = this.footer.getMargins();
9568             height += (this.footer.getHeight()+fm.top+fm.bottom);
9569         }
9570         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9571         height += this.centerBg.getPadding("tb");
9572         return height;
9573     },
9574
9575     // private
9576     syncBodyHeight : function()
9577     {
9578         var bd = this.body, // the text
9579             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9580             bw = this.bwrap;
9581         var height = this.size.height - this.getHeaderFooterHeight(false);
9582         bd.setHeight(height-bd.getMargins("tb"));
9583         var hh = this.header.getHeight();
9584         var h = this.size.height-hh;
9585         cb.setHeight(h);
9586         
9587         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9588         bw.setHeight(h-cb.getPadding("tb"));
9589         
9590         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9591         bd.setWidth(bw.getWidth(true));
9592         if(this.tabs){
9593             this.tabs.syncHeight();
9594             if(Roo.isIE){
9595                 this.tabs.el.repaint();
9596             }
9597         }
9598     },
9599
9600     /**
9601      * Restores the previous state of the dialog if Roo.state is configured.
9602      * @return {Roo.BasicDialog} this
9603      */
9604     restoreState : function(){
9605         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9606         if(box && box.width){
9607             this.xy = [box.x, box.y];
9608             this.resizeTo(box.width, box.height);
9609         }
9610         return this;
9611     },
9612
9613     // private
9614     beforeShow : function(){
9615         this.expand();
9616         if(this.fixedcenter){
9617             this.xy = this.el.getCenterXY(true);
9618         }
9619         if(this.modal){
9620             Roo.get(document.body).addClass("x-body-masked");
9621             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9622             this.mask.show();
9623         }
9624         this.constrainXY();
9625     },
9626
9627     // private
9628     animShow : function(){
9629         var b = Roo.get(this.animateTarget).getBox();
9630         this.proxy.setSize(b.width, b.height);
9631         this.proxy.setLocation(b.x, b.y);
9632         this.proxy.show();
9633         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9634                     true, .35, this.showEl.createDelegate(this));
9635     },
9636
9637     /**
9638      * Shows the dialog.
9639      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9640      * @return {Roo.BasicDialog} this
9641      */
9642     show : function(animateTarget){
9643         if (this.fireEvent("beforeshow", this) === false){
9644             return;
9645         }
9646         if(this.syncHeightBeforeShow){
9647             this.syncBodyHeight();
9648         }else if(this.firstShow){
9649             this.firstShow = false;
9650             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9651         }
9652         this.animateTarget = animateTarget || this.animateTarget;
9653         if(!this.el.isVisible()){
9654             this.beforeShow();
9655             if(this.animateTarget && Roo.get(this.animateTarget)){
9656                 this.animShow();
9657             }else{
9658                 this.showEl();
9659             }
9660         }
9661         return this;
9662     },
9663
9664     // private
9665     showEl : function(){
9666         this.proxy.hide();
9667         this.el.setXY(this.xy);
9668         this.el.show();
9669         this.adjustAssets(true);
9670         this.toFront();
9671         this.focus();
9672         // IE peekaboo bug - fix found by Dave Fenwick
9673         if(Roo.isIE){
9674             this.el.repaint();
9675         }
9676         this.fireEvent("show", this);
9677     },
9678
9679     /**
9680      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9681      * dialog itself will receive focus.
9682      */
9683     focus : function(){
9684         if(this.defaultButton){
9685             this.defaultButton.focus();
9686         }else{
9687             this.focusEl.focus();
9688         }
9689     },
9690
9691     // private
9692     constrainXY : function(){
9693         if(this.constraintoviewport !== false){
9694             if(!this.viewSize){
9695                 if(this.container){
9696                     var s = this.container.getSize();
9697                     this.viewSize = [s.width, s.height];
9698                 }else{
9699                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9700                 }
9701             }
9702             var s = Roo.get(this.container||document).getScroll();
9703
9704             var x = this.xy[0], y = this.xy[1];
9705             var w = this.size.width, h = this.size.height;
9706             var vw = this.viewSize[0], vh = this.viewSize[1];
9707             // only move it if it needs it
9708             var moved = false;
9709             // first validate right/bottom
9710             if(x + w > vw+s.left){
9711                 x = vw - w;
9712                 moved = true;
9713             }
9714             if(y + h > vh+s.top){
9715                 y = vh - h;
9716                 moved = true;
9717             }
9718             // then make sure top/left isn't negative
9719             if(x < s.left){
9720                 x = s.left;
9721                 moved = true;
9722             }
9723             if(y < s.top){
9724                 y = s.top;
9725                 moved = true;
9726             }
9727             if(moved){
9728                 // cache xy
9729                 this.xy = [x, y];
9730                 if(this.isVisible()){
9731                     this.el.setLocation(x, y);
9732                     this.adjustAssets();
9733                 }
9734             }
9735         }
9736     },
9737
9738     // private
9739     onDrag : function(){
9740         if(!this.proxyDrag){
9741             this.xy = this.el.getXY();
9742             this.adjustAssets();
9743         }
9744     },
9745
9746     // private
9747     adjustAssets : function(doShow){
9748         var x = this.xy[0], y = this.xy[1];
9749         var w = this.size.width, h = this.size.height;
9750         if(doShow === true){
9751             if(this.shadow){
9752                 this.shadow.show(this.el);
9753             }
9754             if(this.shim){
9755                 this.shim.show();
9756             }
9757         }
9758         if(this.shadow && this.shadow.isVisible()){
9759             this.shadow.show(this.el);
9760         }
9761         if(this.shim && this.shim.isVisible()){
9762             this.shim.setBounds(x, y, w, h);
9763         }
9764     },
9765
9766     // private
9767     adjustViewport : function(w, h){
9768         if(!w || !h){
9769             w = Roo.lib.Dom.getViewWidth();
9770             h = Roo.lib.Dom.getViewHeight();
9771         }
9772         // cache the size
9773         this.viewSize = [w, h];
9774         if(this.modal && this.mask.isVisible()){
9775             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9776             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9777         }
9778         if(this.isVisible()){
9779             this.constrainXY();
9780         }
9781     },
9782
9783     /**
9784      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9785      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9786      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9787      */
9788     destroy : function(removeEl){
9789         if(this.isVisible()){
9790             this.animateTarget = null;
9791             this.hide();
9792         }
9793         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9794         if(this.tabs){
9795             this.tabs.destroy(removeEl);
9796         }
9797         Roo.destroy(
9798              this.shim,
9799              this.proxy,
9800              this.resizer,
9801              this.close,
9802              this.mask
9803         );
9804         if(this.dd){
9805             this.dd.unreg();
9806         }
9807         if(this.buttons){
9808            for(var i = 0, len = this.buttons.length; i < len; i++){
9809                this.buttons[i].destroy();
9810            }
9811         }
9812         this.el.removeAllListeners();
9813         if(removeEl === true){
9814             this.el.update("");
9815             this.el.remove();
9816         }
9817         Roo.DialogManager.unregister(this);
9818     },
9819
9820     // private
9821     startMove : function(){
9822         if(this.proxyDrag){
9823             this.proxy.show();
9824         }
9825         if(this.constraintoviewport !== false){
9826             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9827         }
9828     },
9829
9830     // private
9831     endMove : function(){
9832         if(!this.proxyDrag){
9833             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9834         }else{
9835             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9836             this.proxy.hide();
9837         }
9838         this.refreshSize();
9839         this.adjustAssets();
9840         this.focus();
9841         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9842     },
9843
9844     /**
9845      * Brings this dialog to the front of any other visible dialogs
9846      * @return {Roo.BasicDialog} this
9847      */
9848     toFront : function(){
9849         Roo.DialogManager.bringToFront(this);
9850         return this;
9851     },
9852
9853     /**
9854      * Sends this dialog to the back (under) of any other visible dialogs
9855      * @return {Roo.BasicDialog} this
9856      */
9857     toBack : function(){
9858         Roo.DialogManager.sendToBack(this);
9859         return this;
9860     },
9861
9862     /**
9863      * Centers this dialog in the viewport
9864      * @return {Roo.BasicDialog} this
9865      */
9866     center : function(){
9867         var xy = this.el.getCenterXY(true);
9868         this.moveTo(xy[0], xy[1]);
9869         return this;
9870     },
9871
9872     /**
9873      * Moves the dialog's top-left corner to the specified point
9874      * @param {Number} x
9875      * @param {Number} y
9876      * @return {Roo.BasicDialog} this
9877      */
9878     moveTo : function(x, y){
9879         this.xy = [x,y];
9880         if(this.isVisible()){
9881             this.el.setXY(this.xy);
9882             this.adjustAssets();
9883         }
9884         return this;
9885     },
9886
9887     /**
9888      * Aligns the dialog to the specified element
9889      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9890      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9891      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9892      * @return {Roo.BasicDialog} this
9893      */
9894     alignTo : function(element, position, offsets){
9895         this.xy = this.el.getAlignToXY(element, position, offsets);
9896         if(this.isVisible()){
9897             this.el.setXY(this.xy);
9898             this.adjustAssets();
9899         }
9900         return this;
9901     },
9902
9903     /**
9904      * Anchors an element to another element and realigns it when the window is resized.
9905      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9906      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9907      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9908      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9909      * is a number, it is used as the buffer delay (defaults to 50ms).
9910      * @return {Roo.BasicDialog} this
9911      */
9912     anchorTo : function(el, alignment, offsets, monitorScroll){
9913         var action = function(){
9914             this.alignTo(el, alignment, offsets);
9915         };
9916         Roo.EventManager.onWindowResize(action, this);
9917         var tm = typeof monitorScroll;
9918         if(tm != 'undefined'){
9919             Roo.EventManager.on(window, 'scroll', action, this,
9920                 {buffer: tm == 'number' ? monitorScroll : 50});
9921         }
9922         action.call(this);
9923         return this;
9924     },
9925
9926     /**
9927      * Returns true if the dialog is visible
9928      * @return {Boolean}
9929      */
9930     isVisible : function(){
9931         return this.el.isVisible();
9932     },
9933
9934     // private
9935     animHide : function(callback){
9936         var b = Roo.get(this.animateTarget).getBox();
9937         this.proxy.show();
9938         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9939         this.el.hide();
9940         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9941                     this.hideEl.createDelegate(this, [callback]));
9942     },
9943
9944     /**
9945      * Hides the dialog.
9946      * @param {Function} callback (optional) Function to call when the dialog is hidden
9947      * @return {Roo.BasicDialog} this
9948      */
9949     hide : function(callback){
9950         if (this.fireEvent("beforehide", this) === false){
9951             return;
9952         }
9953         if(this.shadow){
9954             this.shadow.hide();
9955         }
9956         if(this.shim) {
9957           this.shim.hide();
9958         }
9959         // sometimes animateTarget seems to get set.. causing problems...
9960         // this just double checks..
9961         if(this.animateTarget && Roo.get(this.animateTarget)) {
9962            this.animHide(callback);
9963         }else{
9964             this.el.hide();
9965             this.hideEl(callback);
9966         }
9967         return this;
9968     },
9969
9970     // private
9971     hideEl : function(callback){
9972         this.proxy.hide();
9973         if(this.modal){
9974             this.mask.hide();
9975             Roo.get(document.body).removeClass("x-body-masked");
9976         }
9977         this.fireEvent("hide", this);
9978         if(typeof callback == "function"){
9979             callback();
9980         }
9981     },
9982
9983     // private
9984     hideAction : function(){
9985         this.setLeft("-10000px");
9986         this.setTop("-10000px");
9987         this.setStyle("visibility", "hidden");
9988     },
9989
9990     // private
9991     refreshSize : function(){
9992         this.size = this.el.getSize();
9993         this.xy = this.el.getXY();
9994         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9995     },
9996
9997     // private
9998     // z-index is managed by the DialogManager and may be overwritten at any time
9999     setZIndex : function(index){
10000         if(this.modal){
10001             this.mask.setStyle("z-index", index);
10002         }
10003         if(this.shim){
10004             this.shim.setStyle("z-index", ++index);
10005         }
10006         if(this.shadow){
10007             this.shadow.setZIndex(++index);
10008         }
10009         this.el.setStyle("z-index", ++index);
10010         if(this.proxy){
10011             this.proxy.setStyle("z-index", ++index);
10012         }
10013         if(this.resizer){
10014             this.resizer.proxy.setStyle("z-index", ++index);
10015         }
10016
10017         this.lastZIndex = index;
10018     },
10019
10020     /**
10021      * Returns the element for this dialog
10022      * @return {Roo.Element} The underlying dialog Element
10023      */
10024     getEl : function(){
10025         return this.el;
10026     }
10027 });
10028
10029 /**
10030  * @class Roo.DialogManager
10031  * Provides global access to BasicDialogs that have been created and
10032  * support for z-indexing (layering) multiple open dialogs.
10033  */
10034 Roo.DialogManager = function(){
10035     var list = {};
10036     var accessList = [];
10037     var front = null;
10038
10039     // private
10040     var sortDialogs = function(d1, d2){
10041         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10042     };
10043
10044     // private
10045     var orderDialogs = function(){
10046         accessList.sort(sortDialogs);
10047         var seed = Roo.DialogManager.zseed;
10048         for(var i = 0, len = accessList.length; i < len; i++){
10049             var dlg = accessList[i];
10050             if(dlg){
10051                 dlg.setZIndex(seed + (i*10));
10052             }
10053         }
10054     };
10055
10056     return {
10057         /**
10058          * The starting z-index for BasicDialogs (defaults to 9000)
10059          * @type Number The z-index value
10060          */
10061         zseed : 9000,
10062
10063         // private
10064         register : function(dlg){
10065             list[dlg.id] = dlg;
10066             accessList.push(dlg);
10067         },
10068
10069         // private
10070         unregister : function(dlg){
10071             delete list[dlg.id];
10072             var i=0;
10073             var len=0;
10074             if(!accessList.indexOf){
10075                 for(  i = 0, len = accessList.length; i < len; i++){
10076                     if(accessList[i] == dlg){
10077                         accessList.splice(i, 1);
10078                         return;
10079                     }
10080                 }
10081             }else{
10082                  i = accessList.indexOf(dlg);
10083                 if(i != -1){
10084                     accessList.splice(i, 1);
10085                 }
10086             }
10087         },
10088
10089         /**
10090          * Gets a registered dialog by id
10091          * @param {String/Object} id The id of the dialog or a dialog
10092          * @return {Roo.BasicDialog} this
10093          */
10094         get : function(id){
10095             return typeof id == "object" ? id : list[id];
10096         },
10097
10098         /**
10099          * Brings the specified dialog to the front
10100          * @param {String/Object} dlg The id of the dialog or a dialog
10101          * @return {Roo.BasicDialog} this
10102          */
10103         bringToFront : function(dlg){
10104             dlg = this.get(dlg);
10105             if(dlg != front){
10106                 front = dlg;
10107                 dlg._lastAccess = new Date().getTime();
10108                 orderDialogs();
10109             }
10110             return dlg;
10111         },
10112
10113         /**
10114          * Sends the specified dialog to the back
10115          * @param {String/Object} dlg The id of the dialog or a dialog
10116          * @return {Roo.BasicDialog} this
10117          */
10118         sendToBack : function(dlg){
10119             dlg = this.get(dlg);
10120             dlg._lastAccess = -(new Date().getTime());
10121             orderDialogs();
10122             return dlg;
10123         },
10124
10125         /**
10126          * Hides all dialogs
10127          */
10128         hideAll : function(){
10129             for(var id in list){
10130                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10131                     list[id].hide();
10132                 }
10133             }
10134         }
10135     };
10136 }();
10137
10138 /**
10139  * @class Roo.LayoutDialog
10140  * @extends Roo.BasicDialog
10141  * @children Roo.ContentPanel
10142  * @parent builder none
10143  * Dialog which provides adjustments for working with a layout in a Dialog.
10144  * Add your necessary layout config options to the dialog's config.<br>
10145  * Example usage (including a nested layout):
10146  * <pre><code>
10147 if(!dialog){
10148     dialog = new Roo.LayoutDialog("download-dlg", {
10149         modal: true,
10150         width:600,
10151         height:450,
10152         shadow:true,
10153         minWidth:500,
10154         minHeight:350,
10155         autoTabs:true,
10156         proxyDrag:true,
10157         // layout config merges with the dialog config
10158         center:{
10159             tabPosition: "top",
10160             alwaysShowTabs: true
10161         }
10162     });
10163     dialog.addKeyListener(27, dialog.hide, dialog);
10164     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10165     dialog.addButton("Build It!", this.getDownload, this);
10166
10167     // we can even add nested layouts
10168     var innerLayout = new Roo.BorderLayout("dl-inner", {
10169         east: {
10170             initialSize: 200,
10171             autoScroll:true,
10172             split:true
10173         },
10174         center: {
10175             autoScroll:true
10176         }
10177     });
10178     innerLayout.beginUpdate();
10179     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10180     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10181     innerLayout.endUpdate(true);
10182
10183     var layout = dialog.getLayout();
10184     layout.beginUpdate();
10185     layout.add("center", new Roo.ContentPanel("standard-panel",
10186                         {title: "Download the Source", fitToFrame:true}));
10187     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10188                {title: "Build your own roo.js"}));
10189     layout.getRegion("center").showPanel(sp);
10190     layout.endUpdate();
10191 }
10192 </code></pre>
10193     * @constructor
10194     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10195     * @param {Object} config configuration options
10196   */
10197 Roo.LayoutDialog = function(el, cfg){
10198     
10199     var config=  cfg;
10200     if (typeof(cfg) == 'undefined') {
10201         config = Roo.apply({}, el);
10202         // not sure why we use documentElement here.. - it should always be body.
10203         // IE7 borks horribly if we use documentElement.
10204         // webkit also does not like documentElement - it creates a body element...
10205         el = Roo.get( document.body || document.documentElement ).createChild();
10206         //config.autoCreate = true;
10207     }
10208     
10209     
10210     config.autoTabs = false;
10211     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10212     this.body.setStyle({overflow:"hidden", position:"relative"});
10213     this.layout = new Roo.BorderLayout(this.body.dom, config);
10214     this.layout.monitorWindowResize = false;
10215     this.el.addClass("x-dlg-auto-layout");
10216     // fix case when center region overwrites center function
10217     this.center = Roo.BasicDialog.prototype.center;
10218     this.on("show", this.layout.layout, this.layout, true);
10219     if (config.items) {
10220         var xitems = config.items;
10221         delete config.items;
10222         Roo.each(xitems, this.addxtype, this);
10223     }
10224     
10225     
10226 };
10227 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10228     
10229     
10230     /**
10231      * @cfg {Roo.LayoutRegion} east  
10232      */
10233     /**
10234      * @cfg {Roo.LayoutRegion} west
10235      */
10236     /**
10237      * @cfg {Roo.LayoutRegion} south
10238      */
10239     /**
10240      * @cfg {Roo.LayoutRegion} north
10241      */
10242     /**
10243      * @cfg {Roo.LayoutRegion} center
10244      */
10245     /**
10246      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10247      */
10248     
10249     
10250     /**
10251      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10252      * @deprecated
10253      */
10254     endUpdate : function(){
10255         this.layout.endUpdate();
10256     },
10257
10258     /**
10259      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10260      *  @deprecated
10261      */
10262     beginUpdate : function(){
10263         this.layout.beginUpdate();
10264     },
10265
10266     /**
10267      * Get the BorderLayout for this dialog
10268      * @return {Roo.BorderLayout}
10269      */
10270     getLayout : function(){
10271         return this.layout;
10272     },
10273
10274     showEl : function(){
10275         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10276         if(Roo.isIE7){
10277             this.layout.layout();
10278         }
10279     },
10280
10281     // private
10282     // Use the syncHeightBeforeShow config option to control this automatically
10283     syncBodyHeight : function(){
10284         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10285         if(this.layout){this.layout.layout();}
10286     },
10287     
10288       /**
10289      * Add an xtype element (actually adds to the layout.)
10290      * @return {Object} xdata xtype object data.
10291      */
10292     
10293     addxtype : function(c) {
10294         return this.layout.addxtype(c);
10295     }
10296 });/*
10297  * Based on:
10298  * Ext JS Library 1.1.1
10299  * Copyright(c) 2006-2007, Ext JS, LLC.
10300  *
10301  * Originally Released Under LGPL - original licence link has changed is not relivant.
10302  *
10303  * Fork - LGPL
10304  * <script type="text/javascript">
10305  */
10306  
10307 /**
10308  * @class Roo.MessageBox
10309  * @static
10310  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10311  * Example usage:
10312  *<pre><code>
10313 // Basic alert:
10314 Roo.Msg.alert('Status', 'Changes saved successfully.');
10315
10316 // Prompt for user data:
10317 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10318     if (btn == 'ok'){
10319         // process text value...
10320     }
10321 });
10322
10323 // Show a dialog using config options:
10324 Roo.Msg.show({
10325    title:'Save Changes?',
10326    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10327    buttons: Roo.Msg.YESNOCANCEL,
10328    fn: processResult,
10329    animEl: 'elId'
10330 });
10331 </code></pre>
10332  * @static
10333  */
10334 Roo.MessageBox = function(){
10335     var dlg, opt, mask, waitTimer;
10336     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10337     var buttons, activeTextEl, bwidth;
10338
10339     // private
10340     var handleButton = function(button){
10341         dlg.hide();
10342         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10343     };
10344
10345     // private
10346     var handleHide = function(){
10347         if(opt && opt.cls){
10348             dlg.el.removeClass(opt.cls);
10349         }
10350         if(waitTimer){
10351             Roo.TaskMgr.stop(waitTimer);
10352             waitTimer = null;
10353         }
10354     };
10355
10356     // private
10357     var updateButtons = function(b){
10358         var width = 0;
10359         if(!b){
10360             buttons["ok"].hide();
10361             buttons["cancel"].hide();
10362             buttons["yes"].hide();
10363             buttons["no"].hide();
10364             dlg.footer.dom.style.display = 'none';
10365             return width;
10366         }
10367         dlg.footer.dom.style.display = '';
10368         for(var k in buttons){
10369             if(typeof buttons[k] != "function"){
10370                 if(b[k]){
10371                     buttons[k].show();
10372                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10373                     width += buttons[k].el.getWidth()+15;
10374                 }else{
10375                     buttons[k].hide();
10376                 }
10377             }
10378         }
10379         return width;
10380     };
10381
10382     // private
10383     var handleEsc = function(d, k, e){
10384         if(opt && opt.closable !== false){
10385             dlg.hide();
10386         }
10387         if(e){
10388             e.stopEvent();
10389         }
10390     };
10391
10392     return {
10393         /**
10394          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10395          * @return {Roo.BasicDialog} The BasicDialog element
10396          */
10397         getDialog : function(){
10398            if(!dlg){
10399                 dlg = new Roo.BasicDialog("x-msg-box", {
10400                     autoCreate : true,
10401                     shadow: true,
10402                     draggable: true,
10403                     resizable:false,
10404                     constraintoviewport:false,
10405                     fixedcenter:true,
10406                     collapsible : false,
10407                     shim:true,
10408                     modal: true,
10409                     width:400, height:100,
10410                     buttonAlign:"center",
10411                     closeClick : function(){
10412                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10413                             handleButton("no");
10414                         }else{
10415                             handleButton("cancel");
10416                         }
10417                     }
10418                 });
10419               
10420                 dlg.on("hide", handleHide);
10421                 mask = dlg.mask;
10422                 dlg.addKeyListener(27, handleEsc);
10423                 buttons = {};
10424                 var bt = this.buttonText;
10425                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10426                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10427                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10428                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10429                 bodyEl = dlg.body.createChild({
10430
10431                     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>'
10432                 });
10433                 msgEl = bodyEl.dom.firstChild;
10434                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10435                 textboxEl.enableDisplayMode();
10436                 textboxEl.addKeyListener([10,13], function(){
10437                     if(dlg.isVisible() && opt && opt.buttons){
10438                         if(opt.buttons.ok){
10439                             handleButton("ok");
10440                         }else if(opt.buttons.yes){
10441                             handleButton("yes");
10442                         }
10443                     }
10444                 });
10445                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10446                 textareaEl.enableDisplayMode();
10447                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10448                 progressEl.enableDisplayMode();
10449                 var pf = progressEl.dom.firstChild;
10450                 if (pf) {
10451                     pp = Roo.get(pf.firstChild);
10452                     pp.setHeight(pf.offsetHeight);
10453                 }
10454                 
10455             }
10456             return dlg;
10457         },
10458
10459         /**
10460          * Updates the message box body text
10461          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10462          * the XHTML-compliant non-breaking space character '&amp;#160;')
10463          * @return {Roo.MessageBox} This message box
10464          */
10465         updateText : function(text){
10466             if(!dlg.isVisible() && !opt.width){
10467                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10468             }
10469             msgEl.innerHTML = text || '&#160;';
10470       
10471             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10472             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10473             var w = Math.max(
10474                     Math.min(opt.width || cw , this.maxWidth), 
10475                     Math.max(opt.minWidth || this.minWidth, bwidth)
10476             );
10477             if(opt.prompt){
10478                 activeTextEl.setWidth(w);
10479             }
10480             if(dlg.isVisible()){
10481                 dlg.fixedcenter = false;
10482             }
10483             // to big, make it scroll. = But as usual stupid IE does not support
10484             // !important..
10485             
10486             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10487                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10488                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10489             } else {
10490                 bodyEl.dom.style.height = '';
10491                 bodyEl.dom.style.overflowY = '';
10492             }
10493             if (cw > w) {
10494                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10495             } else {
10496                 bodyEl.dom.style.overflowX = '';
10497             }
10498             
10499             dlg.setContentSize(w, bodyEl.getHeight());
10500             if(dlg.isVisible()){
10501                 dlg.fixedcenter = true;
10502             }
10503             return this;
10504         },
10505
10506         /**
10507          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10508          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10509          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10510          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10511          * @return {Roo.MessageBox} This message box
10512          */
10513         updateProgress : function(value, text){
10514             if(text){
10515                 this.updateText(text);
10516             }
10517             if (pp) { // weird bug on my firefox - for some reason this is not defined
10518                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10519             }
10520             return this;
10521         },        
10522
10523         /**
10524          * Returns true if the message box is currently displayed
10525          * @return {Boolean} True if the message box is visible, else false
10526          */
10527         isVisible : function(){
10528             return dlg && dlg.isVisible();  
10529         },
10530
10531         /**
10532          * Hides the message box if it is displayed
10533          */
10534         hide : function(){
10535             if(this.isVisible()){
10536                 dlg.hide();
10537             }  
10538         },
10539
10540         /**
10541          * Displays a new message box, or reinitializes an existing message box, based on the config options
10542          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10543          * The following config object properties are supported:
10544          * <pre>
10545 Property    Type             Description
10546 ----------  ---------------  ------------------------------------------------------------------------------------
10547 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10548                                    closes (defaults to undefined)
10549 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10550                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10551 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10552                                    progress and wait dialogs will ignore this property and always hide the
10553                                    close button as they can only be closed programmatically.
10554 cls               String           A custom CSS class to apply to the message box element
10555 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10556                                    displayed (defaults to 75)
10557 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10558                                    function will be btn (the name of the button that was clicked, if applicable,
10559                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10560                                    Progress and wait dialogs will ignore this option since they do not respond to
10561                                    user actions and can only be closed programmatically, so any required function
10562                                    should be called by the same code after it closes the dialog.
10563 icon              String           A CSS class that provides a background image to be used as an icon for
10564                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10565 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10566 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10567 modal             Boolean          False to allow user interaction with the page while the message box is
10568                                    displayed (defaults to true)
10569 msg               String           A string that will replace the existing message box body text (defaults
10570                                    to the XHTML-compliant non-breaking space character '&#160;')
10571 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10572 progress          Boolean          True to display a progress bar (defaults to false)
10573 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10574 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10575 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10576 title             String           The title text
10577 value             String           The string value to set into the active textbox element if displayed
10578 wait              Boolean          True to display a progress bar (defaults to false)
10579 width             Number           The width of the dialog in pixels
10580 </pre>
10581          *
10582          * Example usage:
10583          * <pre><code>
10584 Roo.Msg.show({
10585    title: 'Address',
10586    msg: 'Please enter your address:',
10587    width: 300,
10588    buttons: Roo.MessageBox.OKCANCEL,
10589    multiline: true,
10590    fn: saveAddress,
10591    animEl: 'addAddressBtn'
10592 });
10593 </code></pre>
10594          * @param {Object} config Configuration options
10595          * @return {Roo.MessageBox} This message box
10596          */
10597         show : function(options)
10598         {
10599             
10600             // this causes nightmares if you show one dialog after another
10601             // especially on callbacks..
10602              
10603             if(this.isVisible()){
10604                 
10605                 this.hide();
10606                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10607                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10608                 Roo.log("New Dialog Message:" +  options.msg )
10609                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10610                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10611                 
10612             }
10613             var d = this.getDialog();
10614             opt = options;
10615             d.setTitle(opt.title || "&#160;");
10616             d.close.setDisplayed(opt.closable !== false);
10617             activeTextEl = textboxEl;
10618             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10619             if(opt.prompt){
10620                 if(opt.multiline){
10621                     textboxEl.hide();
10622                     textareaEl.show();
10623                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10624                         opt.multiline : this.defaultTextHeight);
10625                     activeTextEl = textareaEl;
10626                 }else{
10627                     textboxEl.show();
10628                     textareaEl.hide();
10629                 }
10630             }else{
10631                 textboxEl.hide();
10632                 textareaEl.hide();
10633             }
10634             progressEl.setDisplayed(opt.progress === true);
10635             this.updateProgress(0);
10636             activeTextEl.dom.value = opt.value || "";
10637             if(opt.prompt){
10638                 dlg.setDefaultButton(activeTextEl);
10639             }else{
10640                 var bs = opt.buttons;
10641                 var db = null;
10642                 if(bs && bs.ok){
10643                     db = buttons["ok"];
10644                 }else if(bs && bs.yes){
10645                     db = buttons["yes"];
10646                 }
10647                 dlg.setDefaultButton(db);
10648             }
10649             bwidth = updateButtons(opt.buttons);
10650             this.updateText(opt.msg);
10651             if(opt.cls){
10652                 d.el.addClass(opt.cls);
10653             }
10654             d.proxyDrag = opt.proxyDrag === true;
10655             d.modal = opt.modal !== false;
10656             d.mask = opt.modal !== false ? mask : false;
10657             if(!d.isVisible()){
10658                 // force it to the end of the z-index stack so it gets a cursor in FF
10659                 document.body.appendChild(dlg.el.dom);
10660                 d.animateTarget = null;
10661                 d.show(options.animEl);
10662             }
10663             dlg.toFront();
10664             return this;
10665         },
10666
10667         /**
10668          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10669          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10670          * and closing the message box when the process is complete.
10671          * @param {String} title The title bar text
10672          * @param {String} msg The message box body text
10673          * @return {Roo.MessageBox} This message box
10674          */
10675         progress : function(title, msg){
10676             this.show({
10677                 title : title,
10678                 msg : msg,
10679                 buttons: false,
10680                 progress:true,
10681                 closable:false,
10682                 minWidth: this.minProgressWidth,
10683                 modal : true
10684             });
10685             return this;
10686         },
10687
10688         /**
10689          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10690          * If a callback function is passed it will be called after the user clicks the button, and the
10691          * id of the button that was clicked will be passed as the only parameter to the callback
10692          * (could also be the top-right close button).
10693          * @param {String} title The title bar text
10694          * @param {String} msg The message box body text
10695          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10696          * @param {Object} scope (optional) The scope of the callback function
10697          * @return {Roo.MessageBox} This message box
10698          */
10699         alert : function(title, msg, fn, scope){
10700             this.show({
10701                 title : title,
10702                 msg : msg,
10703                 buttons: this.OK,
10704                 fn: fn,
10705                 scope : scope,
10706                 modal : true
10707             });
10708             return this;
10709         },
10710
10711         /**
10712          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10713          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10714          * You are responsible for closing the message box when the process is complete.
10715          * @param {String} msg The message box body text
10716          * @param {String} title (optional) The title bar text
10717          * @return {Roo.MessageBox} This message box
10718          */
10719         wait : function(msg, title){
10720             this.show({
10721                 title : title,
10722                 msg : msg,
10723                 buttons: false,
10724                 closable:false,
10725                 progress:true,
10726                 modal:true,
10727                 width:300,
10728                 wait:true
10729             });
10730             waitTimer = Roo.TaskMgr.start({
10731                 run: function(i){
10732                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10733                 },
10734                 interval: 1000
10735             });
10736             return this;
10737         },
10738
10739         /**
10740          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10741          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10742          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10743          * @param {String} title The title bar text
10744          * @param {String} msg The message box body text
10745          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10746          * @param {Object} scope (optional) The scope of the callback function
10747          * @return {Roo.MessageBox} This message box
10748          */
10749         confirm : function(title, msg, fn, scope){
10750             this.show({
10751                 title : title,
10752                 msg : msg,
10753                 buttons: this.YESNO,
10754                 fn: fn,
10755                 scope : scope,
10756                 modal : true
10757             });
10758             return this;
10759         },
10760
10761         /**
10762          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10763          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10764          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10765          * (could also be the top-right close button) and the text that was entered will be passed as the two
10766          * parameters to the callback.
10767          * @param {String} title The title bar text
10768          * @param {String} msg The message box body text
10769          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10770          * @param {Object} scope (optional) The scope of the callback function
10771          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10772          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10773          * @return {Roo.MessageBox} This message box
10774          */
10775         prompt : function(title, msg, fn, scope, multiline){
10776             this.show({
10777                 title : title,
10778                 msg : msg,
10779                 buttons: this.OKCANCEL,
10780                 fn: fn,
10781                 minWidth:250,
10782                 scope : scope,
10783                 prompt:true,
10784                 multiline: multiline,
10785                 modal : true
10786             });
10787             return this;
10788         },
10789
10790         /**
10791          * Button config that displays a single OK button
10792          * @type Object
10793          */
10794         OK : {ok:true},
10795         /**
10796          * Button config that displays Yes and No buttons
10797          * @type Object
10798          */
10799         YESNO : {yes:true, no:true},
10800         /**
10801          * Button config that displays OK and Cancel buttons
10802          * @type Object
10803          */
10804         OKCANCEL : {ok:true, cancel:true},
10805         /**
10806          * Button config that displays Yes, No and Cancel buttons
10807          * @type Object
10808          */
10809         YESNOCANCEL : {yes:true, no:true, cancel:true},
10810
10811         /**
10812          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10813          * @type Number
10814          */
10815         defaultTextHeight : 75,
10816         /**
10817          * The maximum width in pixels of the message box (defaults to 600)
10818          * @type Number
10819          */
10820         maxWidth : 600,
10821         /**
10822          * The minimum width in pixels of the message box (defaults to 100)
10823          * @type Number
10824          */
10825         minWidth : 100,
10826         /**
10827          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10828          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10829          * @type Number
10830          */
10831         minProgressWidth : 250,
10832         /**
10833          * An object containing the default button text strings that can be overriden for localized language support.
10834          * Supported properties are: ok, cancel, yes and no.
10835          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10836          * @type Object
10837          */
10838         buttonText : {
10839             ok : "OK",
10840             cancel : "Cancel",
10841             yes : "Yes",
10842             no : "No"
10843         }
10844     };
10845 }();
10846
10847 /**
10848  * Shorthand for {@link Roo.MessageBox}
10849  */
10850 Roo.Msg = Roo.MessageBox;/*
10851  * Based on:
10852  * Ext JS Library 1.1.1
10853  * Copyright(c) 2006-2007, Ext JS, LLC.
10854  *
10855  * Originally Released Under LGPL - original licence link has changed is not relivant.
10856  *
10857  * Fork - LGPL
10858  * <script type="text/javascript">
10859  */
10860 /**
10861  * @class Roo.QuickTips
10862  * Provides attractive and customizable tooltips for any element.
10863  * @static
10864  */
10865 Roo.QuickTips = function(){
10866     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10867     var ce, bd, xy, dd;
10868     var visible = false, disabled = true, inited = false;
10869     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10870     
10871     var onOver = function(e){
10872         if(disabled){
10873             return;
10874         }
10875         var t = e.getTarget();
10876         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10877             return;
10878         }
10879         if(ce && t == ce.el){
10880             clearTimeout(hideProc);
10881             return;
10882         }
10883         if(t && tagEls[t.id]){
10884             tagEls[t.id].el = t;
10885             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10886             return;
10887         }
10888         var ttp, et = Roo.fly(t);
10889         var ns = cfg.namespace;
10890         if(tm.interceptTitles && t.title){
10891             ttp = t.title;
10892             t.qtip = ttp;
10893             t.removeAttribute("title");
10894             e.preventDefault();
10895         }else{
10896             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10897         }
10898         if(ttp){
10899             showProc = show.defer(tm.showDelay, tm, [{
10900                 el: t, 
10901                 text: ttp.replace(/\\n/g,'<br/>'),
10902                 width: et.getAttributeNS(ns, cfg.width),
10903                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10904                 title: et.getAttributeNS(ns, cfg.title),
10905                     cls: et.getAttributeNS(ns, cfg.cls)
10906             }]);
10907         }
10908     };
10909     
10910     var onOut = function(e){
10911         clearTimeout(showProc);
10912         var t = e.getTarget();
10913         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10914             hideProc = setTimeout(hide, tm.hideDelay);
10915         }
10916     };
10917     
10918     var onMove = function(e){
10919         if(disabled){
10920             return;
10921         }
10922         xy = e.getXY();
10923         xy[1] += 18;
10924         if(tm.trackMouse && ce){
10925             el.setXY(xy);
10926         }
10927     };
10928     
10929     var onDown = function(e){
10930         clearTimeout(showProc);
10931         clearTimeout(hideProc);
10932         if(!e.within(el)){
10933             if(tm.hideOnClick){
10934                 hide();
10935                 tm.disable();
10936                 tm.enable.defer(100, tm);
10937             }
10938         }
10939     };
10940     
10941     var getPad = function(){
10942         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10943     };
10944
10945     var show = function(o){
10946         if(disabled){
10947             return;
10948         }
10949         clearTimeout(dismissProc);
10950         ce = o;
10951         if(removeCls){ // in case manually hidden
10952             el.removeClass(removeCls);
10953             removeCls = null;
10954         }
10955         if(ce.cls){
10956             el.addClass(ce.cls);
10957             removeCls = ce.cls;
10958         }
10959         if(ce.title){
10960             tipTitle.update(ce.title);
10961             tipTitle.show();
10962         }else{
10963             tipTitle.update('');
10964             tipTitle.hide();
10965         }
10966         el.dom.style.width  = tm.maxWidth+'px';
10967         //tipBody.dom.style.width = '';
10968         tipBodyText.update(o.text);
10969         var p = getPad(), w = ce.width;
10970         if(!w){
10971             var td = tipBodyText.dom;
10972             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10973             if(aw > tm.maxWidth){
10974                 w = tm.maxWidth;
10975             }else if(aw < tm.minWidth){
10976                 w = tm.minWidth;
10977             }else{
10978                 w = aw;
10979             }
10980         }
10981         //tipBody.setWidth(w);
10982         el.setWidth(parseInt(w, 10) + p);
10983         if(ce.autoHide === false){
10984             close.setDisplayed(true);
10985             if(dd){
10986                 dd.unlock();
10987             }
10988         }else{
10989             close.setDisplayed(false);
10990             if(dd){
10991                 dd.lock();
10992             }
10993         }
10994         if(xy){
10995             el.avoidY = xy[1]-18;
10996             el.setXY(xy);
10997         }
10998         if(tm.animate){
10999             el.setOpacity(.1);
11000             el.setStyle("visibility", "visible");
11001             el.fadeIn({callback: afterShow});
11002         }else{
11003             afterShow();
11004         }
11005     };
11006     
11007     var afterShow = function(){
11008         if(ce){
11009             el.show();
11010             esc.enable();
11011             if(tm.autoDismiss && ce.autoHide !== false){
11012                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11013             }
11014         }
11015     };
11016     
11017     var hide = function(noanim){
11018         clearTimeout(dismissProc);
11019         clearTimeout(hideProc);
11020         ce = null;
11021         if(el.isVisible()){
11022             esc.disable();
11023             if(noanim !== true && tm.animate){
11024                 el.fadeOut({callback: afterHide});
11025             }else{
11026                 afterHide();
11027             } 
11028         }
11029     };
11030     
11031     var afterHide = function(){
11032         el.hide();
11033         if(removeCls){
11034             el.removeClass(removeCls);
11035             removeCls = null;
11036         }
11037     };
11038     
11039     return {
11040         /**
11041         * @cfg {Number} minWidth
11042         * The minimum width of the quick tip (defaults to 40)
11043         */
11044        minWidth : 40,
11045         /**
11046         * @cfg {Number} maxWidth
11047         * The maximum width of the quick tip (defaults to 300)
11048         */
11049        maxWidth : 300,
11050         /**
11051         * @cfg {Boolean} interceptTitles
11052         * True to automatically use the element's DOM title value if available (defaults to false)
11053         */
11054        interceptTitles : false,
11055         /**
11056         * @cfg {Boolean} trackMouse
11057         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11058         */
11059        trackMouse : false,
11060         /**
11061         * @cfg {Boolean} hideOnClick
11062         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11063         */
11064        hideOnClick : true,
11065         /**
11066         * @cfg {Number} showDelay
11067         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11068         */
11069        showDelay : 500,
11070         /**
11071         * @cfg {Number} hideDelay
11072         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11073         */
11074        hideDelay : 200,
11075         /**
11076         * @cfg {Boolean} autoHide
11077         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11078         * Used in conjunction with hideDelay.
11079         */
11080        autoHide : true,
11081         /**
11082         * @cfg {Boolean}
11083         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11084         * (defaults to true).  Used in conjunction with autoDismissDelay.
11085         */
11086        autoDismiss : true,
11087         /**
11088         * @cfg {Number}
11089         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11090         */
11091        autoDismissDelay : 5000,
11092        /**
11093         * @cfg {Boolean} animate
11094         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11095         */
11096        animate : false,
11097
11098        /**
11099         * @cfg {String} title
11100         * Title text to display (defaults to '').  This can be any valid HTML markup.
11101         */
11102         title: '',
11103        /**
11104         * @cfg {String} text
11105         * Body text to display (defaults to '').  This can be any valid HTML markup.
11106         */
11107         text : '',
11108        /**
11109         * @cfg {String} cls
11110         * A CSS class to apply to the base quick tip element (defaults to '').
11111         */
11112         cls : '',
11113        /**
11114         * @cfg {Number} width
11115         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11116         * minWidth or maxWidth.
11117         */
11118         width : null,
11119
11120     /**
11121      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11122      * or display QuickTips in a page.
11123      */
11124        init : function(){
11125           tm = Roo.QuickTips;
11126           cfg = tm.tagConfig;
11127           if(!inited){
11128               if(!Roo.isReady){ // allow calling of init() before onReady
11129                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11130                   return;
11131               }
11132               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11133               el.fxDefaults = {stopFx: true};
11134               // maximum custom styling
11135               //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>');
11136               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>');              
11137               tipTitle = el.child('h3');
11138               tipTitle.enableDisplayMode("block");
11139               tipBody = el.child('div.x-tip-bd');
11140               tipBodyText = el.child('div.x-tip-bd-inner');
11141               //bdLeft = el.child('div.x-tip-bd-left');
11142               //bdRight = el.child('div.x-tip-bd-right');
11143               close = el.child('div.x-tip-close');
11144               close.enableDisplayMode("block");
11145               close.on("click", hide);
11146               var d = Roo.get(document);
11147               d.on("mousedown", onDown);
11148               d.on("mouseover", onOver);
11149               d.on("mouseout", onOut);
11150               d.on("mousemove", onMove);
11151               esc = d.addKeyListener(27, hide);
11152               esc.disable();
11153               if(Roo.dd.DD){
11154                   dd = el.initDD("default", null, {
11155                       onDrag : function(){
11156                           el.sync();  
11157                       }
11158                   });
11159                   dd.setHandleElId(tipTitle.id);
11160                   dd.lock();
11161               }
11162               inited = true;
11163           }
11164           this.enable(); 
11165        },
11166
11167     /**
11168      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11169      * are supported:
11170      * <pre>
11171 Property    Type                   Description
11172 ----------  ---------------------  ------------------------------------------------------------------------
11173 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11174      * </ul>
11175      * @param {Object} config The config object
11176      */
11177        register : function(config){
11178            var cs = config instanceof Array ? config : arguments;
11179            for(var i = 0, len = cs.length; i < len; i++) {
11180                var c = cs[i];
11181                var target = c.target;
11182                if(target){
11183                    if(target instanceof Array){
11184                        for(var j = 0, jlen = target.length; j < jlen; j++){
11185                            tagEls[target[j]] = c;
11186                        }
11187                    }else{
11188                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11189                    }
11190                }
11191            }
11192        },
11193
11194     /**
11195      * Removes this quick tip from its element and destroys it.
11196      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11197      */
11198        unregister : function(el){
11199            delete tagEls[Roo.id(el)];
11200        },
11201
11202     /**
11203      * Enable this quick tip.
11204      */
11205        enable : function(){
11206            if(inited && disabled){
11207                locks.pop();
11208                if(locks.length < 1){
11209                    disabled = false;
11210                }
11211            }
11212        },
11213
11214     /**
11215      * Disable this quick tip.
11216      */
11217        disable : function(){
11218           disabled = true;
11219           clearTimeout(showProc);
11220           clearTimeout(hideProc);
11221           clearTimeout(dismissProc);
11222           if(ce){
11223               hide(true);
11224           }
11225           locks.push(1);
11226        },
11227
11228     /**
11229      * Returns true if the quick tip is enabled, else false.
11230      */
11231        isEnabled : function(){
11232             return !disabled;
11233        },
11234
11235         // private
11236        tagConfig : {
11237            namespace : "roo", // was ext?? this may break..
11238            alt_namespace : "ext",
11239            attribute : "qtip",
11240            width : "width",
11241            target : "target",
11242            title : "qtitle",
11243            hide : "hide",
11244            cls : "qclass"
11245        }
11246    };
11247 }();
11248
11249 // backwards compat
11250 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11251  * Based on:
11252  * Ext JS Library 1.1.1
11253  * Copyright(c) 2006-2007, Ext JS, LLC.
11254  *
11255  * Originally Released Under LGPL - original licence link has changed is not relivant.
11256  *
11257  * Fork - LGPL
11258  * <script type="text/javascript">
11259  */
11260  
11261
11262 /**
11263  * @class Roo.tree.TreePanel
11264  * @extends Roo.data.Tree
11265  * @cfg {Roo.tree.TreeNode} root The root node
11266  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11267  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11268  * @cfg {Boolean} enableDD true to enable drag and drop
11269  * @cfg {Boolean} enableDrag true to enable just drag
11270  * @cfg {Boolean} enableDrop true to enable just drop
11271  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11272  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11273  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11274  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11275  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11276  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11277  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11278  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11279  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11280  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11281  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11282  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11283  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11284  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11285  * @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>
11286  * @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>
11287  * 
11288  * @constructor
11289  * @param {String/HTMLElement/Element} el The container element
11290  * @param {Object} config
11291  */
11292 Roo.tree.TreePanel = function(el, config){
11293     var root = false;
11294     var loader = false;
11295     if (config.root) {
11296         root = config.root;
11297         delete config.root;
11298     }
11299     if (config.loader) {
11300         loader = config.loader;
11301         delete config.loader;
11302     }
11303     
11304     Roo.apply(this, config);
11305     Roo.tree.TreePanel.superclass.constructor.call(this);
11306     this.el = Roo.get(el);
11307     this.el.addClass('x-tree');
11308     //console.log(root);
11309     if (root) {
11310         this.setRootNode( Roo.factory(root, Roo.tree));
11311     }
11312     if (loader) {
11313         this.loader = Roo.factory(loader, Roo.tree);
11314     }
11315    /**
11316     * Read-only. The id of the container element becomes this TreePanel's id.
11317     */
11318     this.id = this.el.id;
11319     this.addEvents({
11320         /**
11321         * @event beforeload
11322         * Fires before a node is loaded, return false to cancel
11323         * @param {Node} node The node being loaded
11324         */
11325         "beforeload" : true,
11326         /**
11327         * @event load
11328         * Fires when a node is loaded
11329         * @param {Node} node The node that was loaded
11330         */
11331         "load" : true,
11332         /**
11333         * @event textchange
11334         * Fires when the text for a node is changed
11335         * @param {Node} node The node
11336         * @param {String} text The new text
11337         * @param {String} oldText The old text
11338         */
11339         "textchange" : true,
11340         /**
11341         * @event beforeexpand
11342         * Fires before a node is expanded, return false to cancel.
11343         * @param {Node} node The node
11344         * @param {Boolean} deep
11345         * @param {Boolean} anim
11346         */
11347         "beforeexpand" : true,
11348         /**
11349         * @event beforecollapse
11350         * Fires before a node is collapsed, return false to cancel.
11351         * @param {Node} node The node
11352         * @param {Boolean} deep
11353         * @param {Boolean} anim
11354         */
11355         "beforecollapse" : true,
11356         /**
11357         * @event expand
11358         * Fires when a node is expanded
11359         * @param {Node} node The node
11360         */
11361         "expand" : true,
11362         /**
11363         * @event disabledchange
11364         * Fires when the disabled status of a node changes
11365         * @param {Node} node The node
11366         * @param {Boolean} disabled
11367         */
11368         "disabledchange" : true,
11369         /**
11370         * @event collapse
11371         * Fires when a node is collapsed
11372         * @param {Node} node The node
11373         */
11374         "collapse" : true,
11375         /**
11376         * @event beforeclick
11377         * Fires before click processing on a node. Return false to cancel the default action.
11378         * @param {Node} node The node
11379         * @param {Roo.EventObject} e The event object
11380         */
11381         "beforeclick":true,
11382         /**
11383         * @event checkchange
11384         * Fires when a node with a checkbox's checked property changes
11385         * @param {Node} this This node
11386         * @param {Boolean} checked
11387         */
11388         "checkchange":true,
11389         /**
11390         * @event click
11391         * Fires when a node is clicked
11392         * @param {Node} node The node
11393         * @param {Roo.EventObject} e The event object
11394         */
11395         "click":true,
11396         /**
11397         * @event dblclick
11398         * Fires when a node is double clicked
11399         * @param {Node} node The node
11400         * @param {Roo.EventObject} e The event object
11401         */
11402         "dblclick":true,
11403         /**
11404         * @event contextmenu
11405         * Fires when a node is right clicked
11406         * @param {Node} node The node
11407         * @param {Roo.EventObject} e The event object
11408         */
11409         "contextmenu":true,
11410         /**
11411         * @event beforechildrenrendered
11412         * Fires right before the child nodes for a node are rendered
11413         * @param {Node} node The node
11414         */
11415         "beforechildrenrendered":true,
11416         /**
11417         * @event startdrag
11418         * Fires when a node starts being dragged
11419         * @param {Roo.tree.TreePanel} this
11420         * @param {Roo.tree.TreeNode} node
11421         * @param {event} e The raw browser event
11422         */ 
11423        "startdrag" : true,
11424        /**
11425         * @event enddrag
11426         * Fires when a drag operation is complete
11427         * @param {Roo.tree.TreePanel} this
11428         * @param {Roo.tree.TreeNode} node
11429         * @param {event} e The raw browser event
11430         */
11431        "enddrag" : true,
11432        /**
11433         * @event dragdrop
11434         * Fires when a dragged node is dropped on a valid DD target
11435         * @param {Roo.tree.TreePanel} this
11436         * @param {Roo.tree.TreeNode} node
11437         * @param {DD} dd The dd it was dropped on
11438         * @param {event} e The raw browser event
11439         */
11440        "dragdrop" : true,
11441        /**
11442         * @event beforenodedrop
11443         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11444         * passed to handlers has the following properties:<br />
11445         * <ul style="padding:5px;padding-left:16px;">
11446         * <li>tree - The TreePanel</li>
11447         * <li>target - The node being targeted for the drop</li>
11448         * <li>data - The drag data from the drag source</li>
11449         * <li>point - The point of the drop - append, above or below</li>
11450         * <li>source - The drag source</li>
11451         * <li>rawEvent - Raw mouse event</li>
11452         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11453         * to be inserted by setting them on this object.</li>
11454         * <li>cancel - Set this to true to cancel the drop.</li>
11455         * </ul>
11456         * @param {Object} dropEvent
11457         */
11458        "beforenodedrop" : true,
11459        /**
11460         * @event nodedrop
11461         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11462         * passed to handlers has the following properties:<br />
11463         * <ul style="padding:5px;padding-left:16px;">
11464         * <li>tree - The TreePanel</li>
11465         * <li>target - The node being targeted for the drop</li>
11466         * <li>data - The drag data from the drag source</li>
11467         * <li>point - The point of the drop - append, above or below</li>
11468         * <li>source - The drag source</li>
11469         * <li>rawEvent - Raw mouse event</li>
11470         * <li>dropNode - Dropped node(s).</li>
11471         * </ul>
11472         * @param {Object} dropEvent
11473         */
11474        "nodedrop" : true,
11475         /**
11476         * @event nodedragover
11477         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11478         * passed to handlers has the following properties:<br />
11479         * <ul style="padding:5px;padding-left:16px;">
11480         * <li>tree - The TreePanel</li>
11481         * <li>target - The node being targeted for the drop</li>
11482         * <li>data - The drag data from the drag source</li>
11483         * <li>point - The point of the drop - append, above or below</li>
11484         * <li>source - The drag source</li>
11485         * <li>rawEvent - Raw mouse event</li>
11486         * <li>dropNode - Drop node(s) provided by the source.</li>
11487         * <li>cancel - Set this to true to signal drop not allowed.</li>
11488         * </ul>
11489         * @param {Object} dragOverEvent
11490         */
11491        "nodedragover" : true,
11492        /**
11493         * @event appendnode
11494         * Fires when append node to the tree
11495         * @param {Roo.tree.TreePanel} this
11496         * @param {Roo.tree.TreeNode} node
11497         * @param {Number} index The index of the newly appended node
11498         */
11499        "appendnode" : true
11500         
11501     });
11502     if(this.singleExpand){
11503        this.on("beforeexpand", this.restrictExpand, this);
11504     }
11505     if (this.editor) {
11506         this.editor.tree = this;
11507         this.editor = Roo.factory(this.editor, Roo.tree);
11508     }
11509     
11510     if (this.selModel) {
11511         this.selModel = Roo.factory(this.selModel, Roo.tree);
11512     }
11513    
11514 };
11515 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11516     rootVisible : true,
11517     animate: Roo.enableFx,
11518     lines : true,
11519     enableDD : false,
11520     hlDrop : Roo.enableFx,
11521   
11522     renderer: false,
11523     
11524     rendererTip: false,
11525     // private
11526     restrictExpand : function(node){
11527         var p = node.parentNode;
11528         if(p){
11529             if(p.expandedChild && p.expandedChild.parentNode == p){
11530                 p.expandedChild.collapse();
11531             }
11532             p.expandedChild = node;
11533         }
11534     },
11535
11536     // private override
11537     setRootNode : function(node){
11538         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11539         if(!this.rootVisible){
11540             node.ui = new Roo.tree.RootTreeNodeUI(node);
11541         }
11542         return node;
11543     },
11544
11545     /**
11546      * Returns the container element for this TreePanel
11547      */
11548     getEl : function(){
11549         return this.el;
11550     },
11551
11552     /**
11553      * Returns the default TreeLoader for this TreePanel
11554      */
11555     getLoader : function(){
11556         return this.loader;
11557     },
11558
11559     /**
11560      * Expand all nodes
11561      */
11562     expandAll : function(){
11563         this.root.expand(true);
11564     },
11565
11566     /**
11567      * Collapse all nodes
11568      */
11569     collapseAll : function(){
11570         this.root.collapse(true);
11571     },
11572
11573     /**
11574      * Returns the selection model used by this TreePanel
11575      */
11576     getSelectionModel : function(){
11577         if(!this.selModel){
11578             this.selModel = new Roo.tree.DefaultSelectionModel();
11579         }
11580         return this.selModel;
11581     },
11582
11583     /**
11584      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11585      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11586      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11587      * @return {Array}
11588      */
11589     getChecked : function(a, startNode){
11590         startNode = startNode || this.root;
11591         var r = [];
11592         var f = function(){
11593             if(this.attributes.checked){
11594                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11595             }
11596         }
11597         startNode.cascade(f);
11598         return r;
11599     },
11600
11601     /**
11602      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11603      * @param {String} path
11604      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11605      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11606      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11607      */
11608     expandPath : function(path, attr, callback){
11609         attr = attr || "id";
11610         var keys = path.split(this.pathSeparator);
11611         var curNode = this.root;
11612         if(curNode.attributes[attr] != keys[1]){ // invalid root
11613             if(callback){
11614                 callback(false, null);
11615             }
11616             return;
11617         }
11618         var index = 1;
11619         var f = function(){
11620             if(++index == keys.length){
11621                 if(callback){
11622                     callback(true, curNode);
11623                 }
11624                 return;
11625             }
11626             var c = curNode.findChild(attr, keys[index]);
11627             if(!c){
11628                 if(callback){
11629                     callback(false, curNode);
11630                 }
11631                 return;
11632             }
11633             curNode = c;
11634             c.expand(false, false, f);
11635         };
11636         curNode.expand(false, false, f);
11637     },
11638
11639     /**
11640      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11641      * @param {String} path
11642      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11643      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11644      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11645      */
11646     selectPath : function(path, attr, callback){
11647         attr = attr || "id";
11648         var keys = path.split(this.pathSeparator);
11649         var v = keys.pop();
11650         if(keys.length > 0){
11651             var f = function(success, node){
11652                 if(success && node){
11653                     var n = node.findChild(attr, v);
11654                     if(n){
11655                         n.select();
11656                         if(callback){
11657                             callback(true, n);
11658                         }
11659                     }else if(callback){
11660                         callback(false, n);
11661                     }
11662                 }else{
11663                     if(callback){
11664                         callback(false, n);
11665                     }
11666                 }
11667             };
11668             this.expandPath(keys.join(this.pathSeparator), attr, f);
11669         }else{
11670             this.root.select();
11671             if(callback){
11672                 callback(true, this.root);
11673             }
11674         }
11675     },
11676
11677     getTreeEl : function(){
11678         return this.el;
11679     },
11680
11681     /**
11682      * Trigger rendering of this TreePanel
11683      */
11684     render : function(){
11685         if (this.innerCt) {
11686             return this; // stop it rendering more than once!!
11687         }
11688         
11689         this.innerCt = this.el.createChild({tag:"ul",
11690                cls:"x-tree-root-ct " +
11691                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11692
11693         if(this.containerScroll){
11694             Roo.dd.ScrollManager.register(this.el);
11695         }
11696         if((this.enableDD || this.enableDrop) && !this.dropZone){
11697            /**
11698             * The dropZone used by this tree if drop is enabled
11699             * @type Roo.tree.TreeDropZone
11700             */
11701              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11702                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11703            });
11704         }
11705         if((this.enableDD || this.enableDrag) && !this.dragZone){
11706            /**
11707             * The dragZone used by this tree if drag is enabled
11708             * @type Roo.tree.TreeDragZone
11709             */
11710             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11711                ddGroup: this.ddGroup || "TreeDD",
11712                scroll: this.ddScroll
11713            });
11714         }
11715         this.getSelectionModel().init(this);
11716         if (!this.root) {
11717             Roo.log("ROOT not set in tree");
11718             return this;
11719         }
11720         this.root.render();
11721         if(!this.rootVisible){
11722             this.root.renderChildren();
11723         }
11724         return this;
11725     }
11726 });/*
11727  * Based on:
11728  * Ext JS Library 1.1.1
11729  * Copyright(c) 2006-2007, Ext JS, LLC.
11730  *
11731  * Originally Released Under LGPL - original licence link has changed is not relivant.
11732  *
11733  * Fork - LGPL
11734  * <script type="text/javascript">
11735  */
11736  
11737
11738 /**
11739  * @class Roo.tree.DefaultSelectionModel
11740  * @extends Roo.util.Observable
11741  * The default single selection for a TreePanel.
11742  * @param {Object} cfg Configuration
11743  */
11744 Roo.tree.DefaultSelectionModel = function(cfg){
11745    this.selNode = null;
11746    
11747    
11748    
11749    this.addEvents({
11750        /**
11751         * @event selectionchange
11752         * Fires when the selected node changes
11753         * @param {DefaultSelectionModel} this
11754         * @param {TreeNode} node the new selection
11755         */
11756        "selectionchange" : true,
11757
11758        /**
11759         * @event beforeselect
11760         * Fires before the selected node changes, return false to cancel the change
11761         * @param {DefaultSelectionModel} this
11762         * @param {TreeNode} node the new selection
11763         * @param {TreeNode} node the old selection
11764         */
11765        "beforeselect" : true
11766    });
11767    
11768     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11769 };
11770
11771 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11772     init : function(tree){
11773         this.tree = tree;
11774         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11775         tree.on("click", this.onNodeClick, this);
11776     },
11777     
11778     onNodeClick : function(node, e){
11779         if (e.ctrlKey && this.selNode == node)  {
11780             this.unselect(node);
11781             return;
11782         }
11783         this.select(node);
11784     },
11785     
11786     /**
11787      * Select a node.
11788      * @param {TreeNode} node The node to select
11789      * @return {TreeNode} The selected node
11790      */
11791     select : function(node){
11792         var last = this.selNode;
11793         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11794             if(last){
11795                 last.ui.onSelectedChange(false);
11796             }
11797             this.selNode = node;
11798             node.ui.onSelectedChange(true);
11799             this.fireEvent("selectionchange", this, node, last);
11800         }
11801         return node;
11802     },
11803     
11804     /**
11805      * Deselect a node.
11806      * @param {TreeNode} node The node to unselect
11807      */
11808     unselect : function(node){
11809         if(this.selNode == node){
11810             this.clearSelections();
11811         }    
11812     },
11813     
11814     /**
11815      * Clear all selections
11816      */
11817     clearSelections : function(){
11818         var n = this.selNode;
11819         if(n){
11820             n.ui.onSelectedChange(false);
11821             this.selNode = null;
11822             this.fireEvent("selectionchange", this, null);
11823         }
11824         return n;
11825     },
11826     
11827     /**
11828      * Get the selected node
11829      * @return {TreeNode} The selected node
11830      */
11831     getSelectedNode : function(){
11832         return this.selNode;    
11833     },
11834     
11835     /**
11836      * Returns true if the node is selected
11837      * @param {TreeNode} node The node to check
11838      * @return {Boolean}
11839      */
11840     isSelected : function(node){
11841         return this.selNode == node;  
11842     },
11843
11844     /**
11845      * Selects the node above the selected node in the tree, intelligently walking the nodes
11846      * @return TreeNode The new selection
11847      */
11848     selectPrevious : function(){
11849         var s = this.selNode || this.lastSelNode;
11850         if(!s){
11851             return null;
11852         }
11853         var ps = s.previousSibling;
11854         if(ps){
11855             if(!ps.isExpanded() || ps.childNodes.length < 1){
11856                 return this.select(ps);
11857             } else{
11858                 var lc = ps.lastChild;
11859                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11860                     lc = lc.lastChild;
11861                 }
11862                 return this.select(lc);
11863             }
11864         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11865             return this.select(s.parentNode);
11866         }
11867         return null;
11868     },
11869
11870     /**
11871      * Selects the node above the selected node in the tree, intelligently walking the nodes
11872      * @return TreeNode The new selection
11873      */
11874     selectNext : function(){
11875         var s = this.selNode || this.lastSelNode;
11876         if(!s){
11877             return null;
11878         }
11879         if(s.firstChild && s.isExpanded()){
11880              return this.select(s.firstChild);
11881          }else if(s.nextSibling){
11882              return this.select(s.nextSibling);
11883          }else if(s.parentNode){
11884             var newS = null;
11885             s.parentNode.bubble(function(){
11886                 if(this.nextSibling){
11887                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11888                     return false;
11889                 }
11890             });
11891             return newS;
11892          }
11893         return null;
11894     },
11895
11896     onKeyDown : function(e){
11897         var s = this.selNode || this.lastSelNode;
11898         // undesirable, but required
11899         var sm = this;
11900         if(!s){
11901             return;
11902         }
11903         var k = e.getKey();
11904         switch(k){
11905              case e.DOWN:
11906                  e.stopEvent();
11907                  this.selectNext();
11908              break;
11909              case e.UP:
11910                  e.stopEvent();
11911                  this.selectPrevious();
11912              break;
11913              case e.RIGHT:
11914                  e.preventDefault();
11915                  if(s.hasChildNodes()){
11916                      if(!s.isExpanded()){
11917                          s.expand();
11918                      }else if(s.firstChild){
11919                          this.select(s.firstChild, e);
11920                      }
11921                  }
11922              break;
11923              case e.LEFT:
11924                  e.preventDefault();
11925                  if(s.hasChildNodes() && s.isExpanded()){
11926                      s.collapse();
11927                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11928                      this.select(s.parentNode, e);
11929                  }
11930              break;
11931         };
11932     }
11933 });
11934
11935 /**
11936  * @class Roo.tree.MultiSelectionModel
11937  * @extends Roo.util.Observable
11938  * Multi selection for a TreePanel.
11939  * @param {Object} cfg Configuration
11940  */
11941 Roo.tree.MultiSelectionModel = function(){
11942    this.selNodes = [];
11943    this.selMap = {};
11944    this.addEvents({
11945        /**
11946         * @event selectionchange
11947         * Fires when the selected nodes change
11948         * @param {MultiSelectionModel} this
11949         * @param {Array} nodes Array of the selected nodes
11950         */
11951        "selectionchange" : true
11952    });
11953    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11954    
11955 };
11956
11957 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11958     init : function(tree){
11959         this.tree = tree;
11960         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11961         tree.on("click", this.onNodeClick, this);
11962     },
11963     
11964     onNodeClick : function(node, e){
11965         this.select(node, e, e.ctrlKey);
11966     },
11967     
11968     /**
11969      * Select a node.
11970      * @param {TreeNode} node The node to select
11971      * @param {EventObject} e (optional) An event associated with the selection
11972      * @param {Boolean} keepExisting True to retain existing selections
11973      * @return {TreeNode} The selected node
11974      */
11975     select : function(node, e, keepExisting){
11976         if(keepExisting !== true){
11977             this.clearSelections(true);
11978         }
11979         if(this.isSelected(node)){
11980             this.lastSelNode = node;
11981             return node;
11982         }
11983         this.selNodes.push(node);
11984         this.selMap[node.id] = node;
11985         this.lastSelNode = node;
11986         node.ui.onSelectedChange(true);
11987         this.fireEvent("selectionchange", this, this.selNodes);
11988         return node;
11989     },
11990     
11991     /**
11992      * Deselect a node.
11993      * @param {TreeNode} node The node to unselect
11994      */
11995     unselect : function(node){
11996         if(this.selMap[node.id]){
11997             node.ui.onSelectedChange(false);
11998             var sn = this.selNodes;
11999             var index = -1;
12000             if(sn.indexOf){
12001                 index = sn.indexOf(node);
12002             }else{
12003                 for(var i = 0, len = sn.length; i < len; i++){
12004                     if(sn[i] == node){
12005                         index = i;
12006                         break;
12007                     }
12008                 }
12009             }
12010             if(index != -1){
12011                 this.selNodes.splice(index, 1);
12012             }
12013             delete this.selMap[node.id];
12014             this.fireEvent("selectionchange", this, this.selNodes);
12015         }
12016     },
12017     
12018     /**
12019      * Clear all selections
12020      */
12021     clearSelections : function(suppressEvent){
12022         var sn = this.selNodes;
12023         if(sn.length > 0){
12024             for(var i = 0, len = sn.length; i < len; i++){
12025                 sn[i].ui.onSelectedChange(false);
12026             }
12027             this.selNodes = [];
12028             this.selMap = {};
12029             if(suppressEvent !== true){
12030                 this.fireEvent("selectionchange", this, this.selNodes);
12031             }
12032         }
12033     },
12034     
12035     /**
12036      * Returns true if the node is selected
12037      * @param {TreeNode} node The node to check
12038      * @return {Boolean}
12039      */
12040     isSelected : function(node){
12041         return this.selMap[node.id] ? true : false;  
12042     },
12043     
12044     /**
12045      * Returns an array of the selected nodes
12046      * @return {Array}
12047      */
12048     getSelectedNodes : function(){
12049         return this.selNodes;    
12050     },
12051
12052     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12053
12054     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12055
12056     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12057 });/*
12058  * Based on:
12059  * Ext JS Library 1.1.1
12060  * Copyright(c) 2006-2007, Ext JS, LLC.
12061  *
12062  * Originally Released Under LGPL - original licence link has changed is not relivant.
12063  *
12064  * Fork - LGPL
12065  * <script type="text/javascript">
12066  */
12067  
12068 /**
12069  * @class Roo.tree.TreeNode
12070  * @extends Roo.data.Node
12071  * @cfg {String} text The text for this node
12072  * @cfg {Boolean} expanded true to start the node expanded
12073  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12074  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12075  * @cfg {Boolean} disabled true to start the node disabled
12076  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12077  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12078  * @cfg {String} cls A css class to be added to the node
12079  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12080  * @cfg {String} href URL of the link used for the node (defaults to #)
12081  * @cfg {String} hrefTarget target frame for the link
12082  * @cfg {String} qtip An Ext QuickTip for the node
12083  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12084  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12085  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12086  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12087  * (defaults to undefined with no checkbox rendered)
12088  * @constructor
12089  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12090  */
12091 Roo.tree.TreeNode = function(attributes){
12092     attributes = attributes || {};
12093     if(typeof attributes == "string"){
12094         attributes = {text: attributes};
12095     }
12096     this.childrenRendered = false;
12097     this.rendered = false;
12098     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12099     this.expanded = attributes.expanded === true;
12100     this.isTarget = attributes.isTarget !== false;
12101     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12102     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12103
12104     /**
12105      * Read-only. The text for this node. To change it use setText().
12106      * @type String
12107      */
12108     this.text = attributes.text;
12109     /**
12110      * True if this node is disabled.
12111      * @type Boolean
12112      */
12113     this.disabled = attributes.disabled === true;
12114
12115     this.addEvents({
12116         /**
12117         * @event textchange
12118         * Fires when the text for this node is changed
12119         * @param {Node} this This node
12120         * @param {String} text The new text
12121         * @param {String} oldText The old text
12122         */
12123         "textchange" : true,
12124         /**
12125         * @event beforeexpand
12126         * Fires before this node is expanded, return false to cancel.
12127         * @param {Node} this This node
12128         * @param {Boolean} deep
12129         * @param {Boolean} anim
12130         */
12131         "beforeexpand" : true,
12132         /**
12133         * @event beforecollapse
12134         * Fires before this node is collapsed, return false to cancel.
12135         * @param {Node} this This node
12136         * @param {Boolean} deep
12137         * @param {Boolean} anim
12138         */
12139         "beforecollapse" : true,
12140         /**
12141         * @event expand
12142         * Fires when this node is expanded
12143         * @param {Node} this This node
12144         */
12145         "expand" : true,
12146         /**
12147         * @event disabledchange
12148         * Fires when the disabled status of this node changes
12149         * @param {Node} this This node
12150         * @param {Boolean} disabled
12151         */
12152         "disabledchange" : true,
12153         /**
12154         * @event collapse
12155         * Fires when this node is collapsed
12156         * @param {Node} this This node
12157         */
12158         "collapse" : true,
12159         /**
12160         * @event beforeclick
12161         * Fires before click processing. Return false to cancel the default action.
12162         * @param {Node} this This node
12163         * @param {Roo.EventObject} e The event object
12164         */
12165         "beforeclick":true,
12166         /**
12167         * @event checkchange
12168         * Fires when a node with a checkbox's checked property changes
12169         * @param {Node} this This node
12170         * @param {Boolean} checked
12171         */
12172         "checkchange":true,
12173         /**
12174         * @event click
12175         * Fires when this node is clicked
12176         * @param {Node} this This node
12177         * @param {Roo.EventObject} e The event object
12178         */
12179         "click":true,
12180         /**
12181         * @event dblclick
12182         * Fires when this node is double clicked
12183         * @param {Node} this This node
12184         * @param {Roo.EventObject} e The event object
12185         */
12186         "dblclick":true,
12187         /**
12188         * @event contextmenu
12189         * Fires when this node is right clicked
12190         * @param {Node} this This node
12191         * @param {Roo.EventObject} e The event object
12192         */
12193         "contextmenu":true,
12194         /**
12195         * @event beforechildrenrendered
12196         * Fires right before the child nodes for this node are rendered
12197         * @param {Node} this This node
12198         */
12199         "beforechildrenrendered":true
12200     });
12201
12202     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12203
12204     /**
12205      * Read-only. The UI for this node
12206      * @type TreeNodeUI
12207      */
12208     this.ui = new uiClass(this);
12209     
12210     // finally support items[]
12211     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12212         return;
12213     }
12214     
12215     
12216     Roo.each(this.attributes.items, function(c) {
12217         this.appendChild(Roo.factory(c,Roo.Tree));
12218     }, this);
12219     delete this.attributes.items;
12220     
12221     
12222     
12223 };
12224 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12225     preventHScroll: true,
12226     /**
12227      * Returns true if this node is expanded
12228      * @return {Boolean}
12229      */
12230     isExpanded : function(){
12231         return this.expanded;
12232     },
12233
12234     /**
12235      * Returns the UI object for this node
12236      * @return {TreeNodeUI}
12237      */
12238     getUI : function(){
12239         return this.ui;
12240     },
12241
12242     // private override
12243     setFirstChild : function(node){
12244         var of = this.firstChild;
12245         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12246         if(this.childrenRendered && of && node != of){
12247             of.renderIndent(true, true);
12248         }
12249         if(this.rendered){
12250             this.renderIndent(true, true);
12251         }
12252     },
12253
12254     // private override
12255     setLastChild : function(node){
12256         var ol = this.lastChild;
12257         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12258         if(this.childrenRendered && ol && node != ol){
12259             ol.renderIndent(true, true);
12260         }
12261         if(this.rendered){
12262             this.renderIndent(true, true);
12263         }
12264     },
12265
12266     // these methods are overridden to provide lazy rendering support
12267     // private override
12268     appendChild : function()
12269     {
12270         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12271         if(node && this.childrenRendered){
12272             node.render();
12273         }
12274         this.ui.updateExpandIcon();
12275         return node;
12276     },
12277
12278     // private override
12279     removeChild : function(node){
12280         this.ownerTree.getSelectionModel().unselect(node);
12281         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12282         // if it's been rendered remove dom node
12283         if(this.childrenRendered){
12284             node.ui.remove();
12285         }
12286         if(this.childNodes.length < 1){
12287             this.collapse(false, false);
12288         }else{
12289             this.ui.updateExpandIcon();
12290         }
12291         if(!this.firstChild) {
12292             this.childrenRendered = false;
12293         }
12294         return node;
12295     },
12296
12297     // private override
12298     insertBefore : function(node, refNode){
12299         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12300         if(newNode && refNode && this.childrenRendered){
12301             node.render();
12302         }
12303         this.ui.updateExpandIcon();
12304         return newNode;
12305     },
12306
12307     /**
12308      * Sets the text for this node
12309      * @param {String} text
12310      */
12311     setText : function(text){
12312         var oldText = this.text;
12313         this.text = text;
12314         this.attributes.text = text;
12315         if(this.rendered){ // event without subscribing
12316             this.ui.onTextChange(this, text, oldText);
12317         }
12318         this.fireEvent("textchange", this, text, oldText);
12319     },
12320
12321     /**
12322      * Triggers selection of this node
12323      */
12324     select : function(){
12325         this.getOwnerTree().getSelectionModel().select(this);
12326     },
12327
12328     /**
12329      * Triggers deselection of this node
12330      */
12331     unselect : function(){
12332         this.getOwnerTree().getSelectionModel().unselect(this);
12333     },
12334
12335     /**
12336      * Returns true if this node is selected
12337      * @return {Boolean}
12338      */
12339     isSelected : function(){
12340         return this.getOwnerTree().getSelectionModel().isSelected(this);
12341     },
12342
12343     /**
12344      * Expand this node.
12345      * @param {Boolean} deep (optional) True to expand all children as well
12346      * @param {Boolean} anim (optional) false to cancel the default animation
12347      * @param {Function} callback (optional) A callback to be called when
12348      * expanding this node completes (does not wait for deep expand to complete).
12349      * Called with 1 parameter, this node.
12350      */
12351     expand : function(deep, anim, callback){
12352         if(!this.expanded){
12353             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12354                 return;
12355             }
12356             if(!this.childrenRendered){
12357                 this.renderChildren();
12358             }
12359             this.expanded = true;
12360             
12361             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12362                 this.ui.animExpand(function(){
12363                     this.fireEvent("expand", this);
12364                     if(typeof callback == "function"){
12365                         callback(this);
12366                     }
12367                     if(deep === true){
12368                         this.expandChildNodes(true);
12369                     }
12370                 }.createDelegate(this));
12371                 return;
12372             }else{
12373                 this.ui.expand();
12374                 this.fireEvent("expand", this);
12375                 if(typeof callback == "function"){
12376                     callback(this);
12377                 }
12378             }
12379         }else{
12380            if(typeof callback == "function"){
12381                callback(this);
12382            }
12383         }
12384         if(deep === true){
12385             this.expandChildNodes(true);
12386         }
12387     },
12388
12389     isHiddenRoot : function(){
12390         return this.isRoot && !this.getOwnerTree().rootVisible;
12391     },
12392
12393     /**
12394      * Collapse this node.
12395      * @param {Boolean} deep (optional) True to collapse all children as well
12396      * @param {Boolean} anim (optional) false to cancel the default animation
12397      */
12398     collapse : function(deep, anim){
12399         if(this.expanded && !this.isHiddenRoot()){
12400             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12401                 return;
12402             }
12403             this.expanded = false;
12404             if((this.getOwnerTree().animate && anim !== false) || anim){
12405                 this.ui.animCollapse(function(){
12406                     this.fireEvent("collapse", this);
12407                     if(deep === true){
12408                         this.collapseChildNodes(true);
12409                     }
12410                 }.createDelegate(this));
12411                 return;
12412             }else{
12413                 this.ui.collapse();
12414                 this.fireEvent("collapse", this);
12415             }
12416         }
12417         if(deep === true){
12418             var cs = this.childNodes;
12419             for(var i = 0, len = cs.length; i < len; i++) {
12420                 cs[i].collapse(true, false);
12421             }
12422         }
12423     },
12424
12425     // private
12426     delayedExpand : function(delay){
12427         if(!this.expandProcId){
12428             this.expandProcId = this.expand.defer(delay, this);
12429         }
12430     },
12431
12432     // private
12433     cancelExpand : function(){
12434         if(this.expandProcId){
12435             clearTimeout(this.expandProcId);
12436         }
12437         this.expandProcId = false;
12438     },
12439
12440     /**
12441      * Toggles expanded/collapsed state of the node
12442      */
12443     toggle : function(){
12444         if(this.expanded){
12445             this.collapse();
12446         }else{
12447             this.expand();
12448         }
12449     },
12450
12451     /**
12452      * Ensures all parent nodes are expanded
12453      */
12454     ensureVisible : function(callback){
12455         var tree = this.getOwnerTree();
12456         tree.expandPath(this.parentNode.getPath(), false, function(){
12457             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12458             Roo.callback(callback);
12459         }.createDelegate(this));
12460     },
12461
12462     /**
12463      * Expand all child nodes
12464      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12465      */
12466     expandChildNodes : function(deep){
12467         var cs = this.childNodes;
12468         for(var i = 0, len = cs.length; i < len; i++) {
12469                 cs[i].expand(deep);
12470         }
12471     },
12472
12473     /**
12474      * Collapse all child nodes
12475      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12476      */
12477     collapseChildNodes : function(deep){
12478         var cs = this.childNodes;
12479         for(var i = 0, len = cs.length; i < len; i++) {
12480                 cs[i].collapse(deep);
12481         }
12482     },
12483
12484     /**
12485      * Disables this node
12486      */
12487     disable : function(){
12488         this.disabled = true;
12489         this.unselect();
12490         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12491             this.ui.onDisableChange(this, true);
12492         }
12493         this.fireEvent("disabledchange", this, true);
12494     },
12495
12496     /**
12497      * Enables this node
12498      */
12499     enable : function(){
12500         this.disabled = false;
12501         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12502             this.ui.onDisableChange(this, false);
12503         }
12504         this.fireEvent("disabledchange", this, false);
12505     },
12506
12507     // private
12508     renderChildren : function(suppressEvent){
12509         if(suppressEvent !== false){
12510             this.fireEvent("beforechildrenrendered", this);
12511         }
12512         var cs = this.childNodes;
12513         for(var i = 0, len = cs.length; i < len; i++){
12514             cs[i].render(true);
12515         }
12516         this.childrenRendered = true;
12517     },
12518
12519     // private
12520     sort : function(fn, scope){
12521         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12522         if(this.childrenRendered){
12523             var cs = this.childNodes;
12524             for(var i = 0, len = cs.length; i < len; i++){
12525                 cs[i].render(true);
12526             }
12527         }
12528     },
12529
12530     // private
12531     render : function(bulkRender){
12532         this.ui.render(bulkRender);
12533         if(!this.rendered){
12534             this.rendered = true;
12535             if(this.expanded){
12536                 this.expanded = false;
12537                 this.expand(false, false);
12538             }
12539         }
12540     },
12541
12542     // private
12543     renderIndent : function(deep, refresh){
12544         if(refresh){
12545             this.ui.childIndent = null;
12546         }
12547         this.ui.renderIndent();
12548         if(deep === true && this.childrenRendered){
12549             var cs = this.childNodes;
12550             for(var i = 0, len = cs.length; i < len; i++){
12551                 cs[i].renderIndent(true, refresh);
12552             }
12553         }
12554     }
12555 });/*
12556  * Based on:
12557  * Ext JS Library 1.1.1
12558  * Copyright(c) 2006-2007, Ext JS, LLC.
12559  *
12560  * Originally Released Under LGPL - original licence link has changed is not relivant.
12561  *
12562  * Fork - LGPL
12563  * <script type="text/javascript">
12564  */
12565  
12566 /**
12567  * @class Roo.tree.AsyncTreeNode
12568  * @extends Roo.tree.TreeNode
12569  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12570  * @constructor
12571  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12572  */
12573  Roo.tree.AsyncTreeNode = function(config){
12574     this.loaded = false;
12575     this.loading = false;
12576     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12577     /**
12578     * @event beforeload
12579     * Fires before this node is loaded, return false to cancel
12580     * @param {Node} this This node
12581     */
12582     this.addEvents({'beforeload':true, 'load': true});
12583     /**
12584     * @event load
12585     * Fires when this node is loaded
12586     * @param {Node} this This node
12587     */
12588     /**
12589      * The loader used by this node (defaults to using the tree's defined loader)
12590      * @type TreeLoader
12591      * @property loader
12592      */
12593 };
12594 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12595     expand : function(deep, anim, callback){
12596         if(this.loading){ // if an async load is already running, waiting til it's done
12597             var timer;
12598             var f = function(){
12599                 if(!this.loading){ // done loading
12600                     clearInterval(timer);
12601                     this.expand(deep, anim, callback);
12602                 }
12603             }.createDelegate(this);
12604             timer = setInterval(f, 200);
12605             return;
12606         }
12607         if(!this.loaded){
12608             if(this.fireEvent("beforeload", this) === false){
12609                 return;
12610             }
12611             this.loading = true;
12612             this.ui.beforeLoad(this);
12613             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12614             if(loader){
12615                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12616                 return;
12617             }
12618         }
12619         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12620     },
12621     
12622     /**
12623      * Returns true if this node is currently loading
12624      * @return {Boolean}
12625      */
12626     isLoading : function(){
12627         return this.loading;  
12628     },
12629     
12630     loadComplete : function(deep, anim, callback){
12631         this.loading = false;
12632         this.loaded = true;
12633         this.ui.afterLoad(this);
12634         this.fireEvent("load", this);
12635         this.expand(deep, anim, callback);
12636     },
12637     
12638     /**
12639      * Returns true if this node has been loaded
12640      * @return {Boolean}
12641      */
12642     isLoaded : function(){
12643         return this.loaded;
12644     },
12645     
12646     hasChildNodes : function(){
12647         if(!this.isLeaf() && !this.loaded){
12648             return true;
12649         }else{
12650             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12651         }
12652     },
12653
12654     /**
12655      * Trigger a reload for this node
12656      * @param {Function} callback
12657      */
12658     reload : function(callback){
12659         this.collapse(false, false);
12660         while(this.firstChild){
12661             this.removeChild(this.firstChild);
12662         }
12663         this.childrenRendered = false;
12664         this.loaded = false;
12665         if(this.isHiddenRoot()){
12666             this.expanded = false;
12667         }
12668         this.expand(false, false, callback);
12669     }
12670 });/*
12671  * Based on:
12672  * Ext JS Library 1.1.1
12673  * Copyright(c) 2006-2007, Ext JS, LLC.
12674  *
12675  * Originally Released Under LGPL - original licence link has changed is not relivant.
12676  *
12677  * Fork - LGPL
12678  * <script type="text/javascript">
12679  */
12680  
12681 /**
12682  * @class Roo.tree.TreeNodeUI
12683  * @constructor
12684  * @param {Object} node The node to render
12685  * The TreeNode UI implementation is separate from the
12686  * tree implementation. Unless you are customizing the tree UI,
12687  * you should never have to use this directly.
12688  */
12689 Roo.tree.TreeNodeUI = function(node){
12690     this.node = node;
12691     this.rendered = false;
12692     this.animating = false;
12693     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12694 };
12695
12696 Roo.tree.TreeNodeUI.prototype = {
12697     removeChild : function(node){
12698         if(this.rendered){
12699             this.ctNode.removeChild(node.ui.getEl());
12700         }
12701     },
12702
12703     beforeLoad : function(){
12704          this.addClass("x-tree-node-loading");
12705     },
12706
12707     afterLoad : function(){
12708          this.removeClass("x-tree-node-loading");
12709     },
12710
12711     onTextChange : function(node, text, oldText){
12712         if(this.rendered){
12713             this.textNode.innerHTML = text;
12714         }
12715     },
12716
12717     onDisableChange : function(node, state){
12718         this.disabled = state;
12719         if(state){
12720             this.addClass("x-tree-node-disabled");
12721         }else{
12722             this.removeClass("x-tree-node-disabled");
12723         }
12724     },
12725
12726     onSelectedChange : function(state){
12727         if(state){
12728             this.focus();
12729             this.addClass("x-tree-selected");
12730         }else{
12731             //this.blur();
12732             this.removeClass("x-tree-selected");
12733         }
12734     },
12735
12736     onMove : function(tree, node, oldParent, newParent, index, refNode){
12737         this.childIndent = null;
12738         if(this.rendered){
12739             var targetNode = newParent.ui.getContainer();
12740             if(!targetNode){//target not rendered
12741                 this.holder = document.createElement("div");
12742                 this.holder.appendChild(this.wrap);
12743                 return;
12744             }
12745             var insertBefore = refNode ? refNode.ui.getEl() : null;
12746             if(insertBefore){
12747                 targetNode.insertBefore(this.wrap, insertBefore);
12748             }else{
12749                 targetNode.appendChild(this.wrap);
12750             }
12751             this.node.renderIndent(true);
12752         }
12753     },
12754
12755     addClass : function(cls){
12756         if(this.elNode){
12757             Roo.fly(this.elNode).addClass(cls);
12758         }
12759     },
12760
12761     removeClass : function(cls){
12762         if(this.elNode){
12763             Roo.fly(this.elNode).removeClass(cls);
12764         }
12765     },
12766
12767     remove : function(){
12768         if(this.rendered){
12769             this.holder = document.createElement("div");
12770             this.holder.appendChild(this.wrap);
12771         }
12772     },
12773
12774     fireEvent : function(){
12775         return this.node.fireEvent.apply(this.node, arguments);
12776     },
12777
12778     initEvents : function(){
12779         this.node.on("move", this.onMove, this);
12780         var E = Roo.EventManager;
12781         var a = this.anchor;
12782
12783         var el = Roo.fly(a, '_treeui');
12784
12785         if(Roo.isOpera){ // opera render bug ignores the CSS
12786             el.setStyle("text-decoration", "none");
12787         }
12788
12789         el.on("click", this.onClick, this);
12790         el.on("dblclick", this.onDblClick, this);
12791
12792         if(this.checkbox){
12793             Roo.EventManager.on(this.checkbox,
12794                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12795         }
12796
12797         el.on("contextmenu", this.onContextMenu, this);
12798
12799         var icon = Roo.fly(this.iconNode);
12800         icon.on("click", this.onClick, this);
12801         icon.on("dblclick", this.onDblClick, this);
12802         icon.on("contextmenu", this.onContextMenu, this);
12803         E.on(this.ecNode, "click", this.ecClick, this, true);
12804
12805         if(this.node.disabled){
12806             this.addClass("x-tree-node-disabled");
12807         }
12808         if(this.node.hidden){
12809             this.addClass("x-tree-node-disabled");
12810         }
12811         var ot = this.node.getOwnerTree();
12812         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12813         if(dd && (!this.node.isRoot || ot.rootVisible)){
12814             Roo.dd.Registry.register(this.elNode, {
12815                 node: this.node,
12816                 handles: this.getDDHandles(),
12817                 isHandle: false
12818             });
12819         }
12820     },
12821
12822     getDDHandles : function(){
12823         return [this.iconNode, this.textNode];
12824     },
12825
12826     hide : function(){
12827         if(this.rendered){
12828             this.wrap.style.display = "none";
12829         }
12830     },
12831
12832     show : function(){
12833         if(this.rendered){
12834             this.wrap.style.display = "";
12835         }
12836     },
12837
12838     onContextMenu : function(e){
12839         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12840             e.preventDefault();
12841             this.focus();
12842             this.fireEvent("contextmenu", this.node, e);
12843         }
12844     },
12845
12846     onClick : function(e){
12847         if(this.dropping){
12848             e.stopEvent();
12849             return;
12850         }
12851         if(this.fireEvent("beforeclick", this.node, e) !== false){
12852             if(!this.disabled && this.node.attributes.href){
12853                 this.fireEvent("click", this.node, e);
12854                 return;
12855             }
12856             e.preventDefault();
12857             if(this.disabled){
12858                 return;
12859             }
12860
12861             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12862                 this.node.toggle();
12863             }
12864
12865             this.fireEvent("click", this.node, e);
12866         }else{
12867             e.stopEvent();
12868         }
12869     },
12870
12871     onDblClick : function(e){
12872         e.preventDefault();
12873         if(this.disabled){
12874             return;
12875         }
12876         if(this.checkbox){
12877             this.toggleCheck();
12878         }
12879         if(!this.animating && this.node.hasChildNodes()){
12880             this.node.toggle();
12881         }
12882         this.fireEvent("dblclick", this.node, e);
12883     },
12884
12885     onCheckChange : function(){
12886         var checked = this.checkbox.checked;
12887         this.node.attributes.checked = checked;
12888         this.fireEvent('checkchange', this.node, checked);
12889     },
12890
12891     ecClick : function(e){
12892         if(!this.animating && this.node.hasChildNodes()){
12893             this.node.toggle();
12894         }
12895     },
12896
12897     startDrop : function(){
12898         this.dropping = true;
12899     },
12900
12901     // delayed drop so the click event doesn't get fired on a drop
12902     endDrop : function(){
12903        setTimeout(function(){
12904            this.dropping = false;
12905        }.createDelegate(this), 50);
12906     },
12907
12908     expand : function(){
12909         this.updateExpandIcon();
12910         this.ctNode.style.display = "";
12911     },
12912
12913     focus : function(){
12914         if(!this.node.preventHScroll){
12915             try{this.anchor.focus();
12916             }catch(e){}
12917         }else if(!Roo.isIE){
12918             try{
12919                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12920                 var l = noscroll.scrollLeft;
12921                 this.anchor.focus();
12922                 noscroll.scrollLeft = l;
12923             }catch(e){}
12924         }
12925     },
12926
12927     toggleCheck : function(value){
12928         var cb = this.checkbox;
12929         if(cb){
12930             cb.checked = (value === undefined ? !cb.checked : value);
12931         }
12932     },
12933
12934     blur : function(){
12935         try{
12936             this.anchor.blur();
12937         }catch(e){}
12938     },
12939
12940     animExpand : function(callback){
12941         var ct = Roo.get(this.ctNode);
12942         ct.stopFx();
12943         if(!this.node.hasChildNodes()){
12944             this.updateExpandIcon();
12945             this.ctNode.style.display = "";
12946             Roo.callback(callback);
12947             return;
12948         }
12949         this.animating = true;
12950         this.updateExpandIcon();
12951
12952         ct.slideIn('t', {
12953            callback : function(){
12954                this.animating = false;
12955                Roo.callback(callback);
12956             },
12957             scope: this,
12958             duration: this.node.ownerTree.duration || .25
12959         });
12960     },
12961
12962     highlight : function(){
12963         var tree = this.node.getOwnerTree();
12964         Roo.fly(this.wrap).highlight(
12965             tree.hlColor || "C3DAF9",
12966             {endColor: tree.hlBaseColor}
12967         );
12968     },
12969
12970     collapse : function(){
12971         this.updateExpandIcon();
12972         this.ctNode.style.display = "none";
12973     },
12974
12975     animCollapse : function(callback){
12976         var ct = Roo.get(this.ctNode);
12977         ct.enableDisplayMode('block');
12978         ct.stopFx();
12979
12980         this.animating = true;
12981         this.updateExpandIcon();
12982
12983         ct.slideOut('t', {
12984             callback : function(){
12985                this.animating = false;
12986                Roo.callback(callback);
12987             },
12988             scope: this,
12989             duration: this.node.ownerTree.duration || .25
12990         });
12991     },
12992
12993     getContainer : function(){
12994         return this.ctNode;
12995     },
12996
12997     getEl : function(){
12998         return this.wrap;
12999     },
13000
13001     appendDDGhost : function(ghostNode){
13002         ghostNode.appendChild(this.elNode.cloneNode(true));
13003     },
13004
13005     getDDRepairXY : function(){
13006         return Roo.lib.Dom.getXY(this.iconNode);
13007     },
13008
13009     onRender : function(){
13010         this.render();
13011     },
13012
13013     render : function(bulkRender){
13014         var n = this.node, a = n.attributes;
13015         var targetNode = n.parentNode ?
13016               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13017
13018         if(!this.rendered){
13019             this.rendered = true;
13020
13021             this.renderElements(n, a, targetNode, bulkRender);
13022
13023             if(a.qtip){
13024                if(this.textNode.setAttributeNS){
13025                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13026                    if(a.qtipTitle){
13027                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13028                    }
13029                }else{
13030                    this.textNode.setAttribute("ext:qtip", a.qtip);
13031                    if(a.qtipTitle){
13032                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13033                    }
13034                }
13035             }else if(a.qtipCfg){
13036                 a.qtipCfg.target = Roo.id(this.textNode);
13037                 Roo.QuickTips.register(a.qtipCfg);
13038             }
13039             this.initEvents();
13040             if(!this.node.expanded){
13041                 this.updateExpandIcon();
13042             }
13043         }else{
13044             if(bulkRender === true) {
13045                 targetNode.appendChild(this.wrap);
13046             }
13047         }
13048     },
13049
13050     renderElements : function(n, a, targetNode, bulkRender)
13051     {
13052         // add some indent caching, this helps performance when rendering a large tree
13053         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13054         var t = n.getOwnerTree();
13055         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13056         if (typeof(n.attributes.html) != 'undefined') {
13057             txt = n.attributes.html;
13058         }
13059         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13060         var cb = typeof a.checked == 'boolean';
13061         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13062         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13063             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13064             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13065             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13066             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13067             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13068              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13069                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13070             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13071             "</li>"];
13072
13073         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13074             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13075                                 n.nextSibling.ui.getEl(), buf.join(""));
13076         }else{
13077             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13078         }
13079
13080         this.elNode = this.wrap.childNodes[0];
13081         this.ctNode = this.wrap.childNodes[1];
13082         var cs = this.elNode.childNodes;
13083         this.indentNode = cs[0];
13084         this.ecNode = cs[1];
13085         this.iconNode = cs[2];
13086         var index = 3;
13087         if(cb){
13088             this.checkbox = cs[3];
13089             index++;
13090         }
13091         this.anchor = cs[index];
13092         this.textNode = cs[index].firstChild;
13093     },
13094
13095     getAnchor : function(){
13096         return this.anchor;
13097     },
13098
13099     getTextEl : function(){
13100         return this.textNode;
13101     },
13102
13103     getIconEl : function(){
13104         return this.iconNode;
13105     },
13106
13107     isChecked : function(){
13108         return this.checkbox ? this.checkbox.checked : false;
13109     },
13110
13111     updateExpandIcon : function(){
13112         if(this.rendered){
13113             var n = this.node, c1, c2;
13114             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13115             var hasChild = n.hasChildNodes();
13116             if(hasChild){
13117                 if(n.expanded){
13118                     cls += "-minus";
13119                     c1 = "x-tree-node-collapsed";
13120                     c2 = "x-tree-node-expanded";
13121                 }else{
13122                     cls += "-plus";
13123                     c1 = "x-tree-node-expanded";
13124                     c2 = "x-tree-node-collapsed";
13125                 }
13126                 if(this.wasLeaf){
13127                     this.removeClass("x-tree-node-leaf");
13128                     this.wasLeaf = false;
13129                 }
13130                 if(this.c1 != c1 || this.c2 != c2){
13131                     Roo.fly(this.elNode).replaceClass(c1, c2);
13132                     this.c1 = c1; this.c2 = c2;
13133                 }
13134             }else{
13135                 // this changes non-leafs into leafs if they have no children.
13136                 // it's not very rational behaviour..
13137                 
13138                 if(!this.wasLeaf && this.node.leaf){
13139                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13140                     delete this.c1;
13141                     delete this.c2;
13142                     this.wasLeaf = true;
13143                 }
13144             }
13145             var ecc = "x-tree-ec-icon "+cls;
13146             if(this.ecc != ecc){
13147                 this.ecNode.className = ecc;
13148                 this.ecc = ecc;
13149             }
13150         }
13151     },
13152
13153     getChildIndent : function(){
13154         if(!this.childIndent){
13155             var buf = [];
13156             var p = this.node;
13157             while(p){
13158                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13159                     if(!p.isLast()) {
13160                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13161                     } else {
13162                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13163                     }
13164                 }
13165                 p = p.parentNode;
13166             }
13167             this.childIndent = buf.join("");
13168         }
13169         return this.childIndent;
13170     },
13171
13172     renderIndent : function(){
13173         if(this.rendered){
13174             var indent = "";
13175             var p = this.node.parentNode;
13176             if(p){
13177                 indent = p.ui.getChildIndent();
13178             }
13179             if(this.indentMarkup != indent){ // don't rerender if not required
13180                 this.indentNode.innerHTML = indent;
13181                 this.indentMarkup = indent;
13182             }
13183             this.updateExpandIcon();
13184         }
13185     }
13186 };
13187
13188 Roo.tree.RootTreeNodeUI = function(){
13189     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13190 };
13191 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13192     render : function(){
13193         if(!this.rendered){
13194             var targetNode = this.node.ownerTree.innerCt.dom;
13195             this.node.expanded = true;
13196             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13197             this.wrap = this.ctNode = targetNode.firstChild;
13198         }
13199     },
13200     collapse : function(){
13201     },
13202     expand : function(){
13203     }
13204 });/*
13205  * Based on:
13206  * Ext JS Library 1.1.1
13207  * Copyright(c) 2006-2007, Ext JS, LLC.
13208  *
13209  * Originally Released Under LGPL - original licence link has changed is not relivant.
13210  *
13211  * Fork - LGPL
13212  * <script type="text/javascript">
13213  */
13214 /**
13215  * @class Roo.tree.TreeLoader
13216  * @extends Roo.util.Observable
13217  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13218  * nodes from a specified URL. The response must be a javascript Array definition
13219  * who's elements are node definition objects. eg:
13220  * <pre><code>
13221 {  success : true,
13222    data :      [
13223    
13224     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13225     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13226     ]
13227 }
13228
13229
13230 </code></pre>
13231  * <br><br>
13232  * The old style respose with just an array is still supported, but not recommended.
13233  * <br><br>
13234  *
13235  * A server request is sent, and child nodes are loaded only when a node is expanded.
13236  * The loading node's id is passed to the server under the parameter name "node" to
13237  * enable the server to produce the correct child nodes.
13238  * <br><br>
13239  * To pass extra parameters, an event handler may be attached to the "beforeload"
13240  * event, and the parameters specified in the TreeLoader's baseParams property:
13241  * <pre><code>
13242     myTreeLoader.on("beforeload", function(treeLoader, node) {
13243         this.baseParams.category = node.attributes.category;
13244     }, this);
13245     
13246 </code></pre>
13247  *
13248  * This would pass an HTTP parameter called "category" to the server containing
13249  * the value of the Node's "category" attribute.
13250  * @constructor
13251  * Creates a new Treeloader.
13252  * @param {Object} config A config object containing config properties.
13253  */
13254 Roo.tree.TreeLoader = function(config){
13255     this.baseParams = {};
13256     this.requestMethod = "POST";
13257     Roo.apply(this, config);
13258
13259     this.addEvents({
13260     
13261         /**
13262          * @event beforeload
13263          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13264          * @param {Object} This TreeLoader object.
13265          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13266          * @param {Object} callback The callback function specified in the {@link #load} call.
13267          */
13268         beforeload : true,
13269         /**
13270          * @event load
13271          * Fires when the node has been successfuly loaded.
13272          * @param {Object} This TreeLoader object.
13273          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13274          * @param {Object} response The response object containing the data from the server.
13275          */
13276         load : true,
13277         /**
13278          * @event loadexception
13279          * Fires if the network request failed.
13280          * @param {Object} This TreeLoader object.
13281          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13282          * @param {Object} response The response object containing the data from the server.
13283          */
13284         loadexception : true,
13285         /**
13286          * @event create
13287          * Fires before a node is created, enabling you to return custom Node types 
13288          * @param {Object} This TreeLoader object.
13289          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13290          */
13291         create : true
13292     });
13293
13294     Roo.tree.TreeLoader.superclass.constructor.call(this);
13295 };
13296
13297 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13298     /**
13299     * @cfg {String} dataUrl The URL from which to request a Json string which
13300     * specifies an array of node definition object representing the child nodes
13301     * to be loaded.
13302     */
13303     /**
13304     * @cfg {String} requestMethod either GET or POST
13305     * defaults to POST (due to BC)
13306     * to be loaded.
13307     */
13308     /**
13309     * @cfg {Object} baseParams (optional) An object containing properties which
13310     * specify HTTP parameters to be passed to each request for child nodes.
13311     */
13312     /**
13313     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13314     * created by this loader. If the attributes sent by the server have an attribute in this object,
13315     * they take priority.
13316     */
13317     /**
13318     * @cfg {Object} uiProviders (optional) An object containing properties which
13319     * 
13320     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13321     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13322     * <i>uiProvider</i> attribute of a returned child node is a string rather
13323     * than a reference to a TreeNodeUI implementation, this that string value
13324     * is used as a property name in the uiProviders object. You can define the provider named
13325     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13326     */
13327     uiProviders : {},
13328
13329     /**
13330     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13331     * child nodes before loading.
13332     */
13333     clearOnLoad : true,
13334
13335     /**
13336     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13337     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13338     * Grid query { data : [ .....] }
13339     */
13340     
13341     root : false,
13342      /**
13343     * @cfg {String} queryParam (optional) 
13344     * Name of the query as it will be passed on the querystring (defaults to 'node')
13345     * eg. the request will be ?node=[id]
13346     */
13347     
13348     
13349     queryParam: false,
13350     
13351     /**
13352      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13353      * This is called automatically when a node is expanded, but may be used to reload
13354      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13355      * @param {Roo.tree.TreeNode} node
13356      * @param {Function} callback
13357      */
13358     load : function(node, callback){
13359         if(this.clearOnLoad){
13360             while(node.firstChild){
13361                 node.removeChild(node.firstChild);
13362             }
13363         }
13364         if(node.attributes.children){ // preloaded json children
13365             var cs = node.attributes.children;
13366             for(var i = 0, len = cs.length; i < len; i++){
13367                 node.appendChild(this.createNode(cs[i]));
13368             }
13369             if(typeof callback == "function"){
13370                 callback();
13371             }
13372         }else if(this.dataUrl){
13373             this.requestData(node, callback);
13374         }
13375     },
13376
13377     getParams: function(node){
13378         var buf = [], bp = this.baseParams;
13379         for(var key in bp){
13380             if(typeof bp[key] != "function"){
13381                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13382             }
13383         }
13384         var n = this.queryParam === false ? 'node' : this.queryParam;
13385         buf.push(n + "=", encodeURIComponent(node.id));
13386         return buf.join("");
13387     },
13388
13389     requestData : function(node, callback){
13390         if(this.fireEvent("beforeload", this, node, callback) !== false){
13391             this.transId = Roo.Ajax.request({
13392                 method:this.requestMethod,
13393                 url: this.dataUrl||this.url,
13394                 success: this.handleResponse,
13395                 failure: this.handleFailure,
13396                 scope: this,
13397                 argument: {callback: callback, node: node},
13398                 params: this.getParams(node)
13399             });
13400         }else{
13401             // if the load is cancelled, make sure we notify
13402             // the node that we are done
13403             if(typeof callback == "function"){
13404                 callback();
13405             }
13406         }
13407     },
13408
13409     isLoading : function(){
13410         return this.transId ? true : false;
13411     },
13412
13413     abort : function(){
13414         if(this.isLoading()){
13415             Roo.Ajax.abort(this.transId);
13416         }
13417     },
13418
13419     // private
13420     createNode : function(attr)
13421     {
13422         // apply baseAttrs, nice idea Corey!
13423         if(this.baseAttrs){
13424             Roo.applyIf(attr, this.baseAttrs);
13425         }
13426         if(this.applyLoader !== false){
13427             attr.loader = this;
13428         }
13429         // uiProvider = depreciated..
13430         
13431         if(typeof(attr.uiProvider) == 'string'){
13432            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13433                 /**  eval:var:attr */ eval(attr.uiProvider);
13434         }
13435         if(typeof(this.uiProviders['default']) != 'undefined') {
13436             attr.uiProvider = this.uiProviders['default'];
13437         }
13438         
13439         this.fireEvent('create', this, attr);
13440         
13441         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13442         return(attr.leaf ?
13443                         new Roo.tree.TreeNode(attr) :
13444                         new Roo.tree.AsyncTreeNode(attr));
13445     },
13446
13447     processResponse : function(response, node, callback)
13448     {
13449         var json = response.responseText;
13450         try {
13451             
13452             var o = Roo.decode(json);
13453             
13454             if (this.root === false && typeof(o.success) != undefined) {
13455                 this.root = 'data'; // the default behaviour for list like data..
13456                 }
13457                 
13458             if (this.root !== false &&  !o.success) {
13459                 // it's a failure condition.
13460                 var a = response.argument;
13461                 this.fireEvent("loadexception", this, a.node, response);
13462                 Roo.log("Load failed - should have a handler really");
13463                 return;
13464             }
13465             
13466             
13467             
13468             if (this.root !== false) {
13469                  o = o[this.root];
13470             }
13471             
13472             for(var i = 0, len = o.length; i < len; i++){
13473                 var n = this.createNode(o[i]);
13474                 if(n){
13475                     node.appendChild(n);
13476                 }
13477             }
13478             if(typeof callback == "function"){
13479                 callback(this, node);
13480             }
13481         }catch(e){
13482             this.handleFailure(response);
13483         }
13484     },
13485
13486     handleResponse : function(response){
13487         this.transId = false;
13488         var a = response.argument;
13489         this.processResponse(response, a.node, a.callback);
13490         this.fireEvent("load", this, a.node, response);
13491     },
13492
13493     handleFailure : function(response)
13494     {
13495         // should handle failure better..
13496         this.transId = false;
13497         var a = response.argument;
13498         this.fireEvent("loadexception", this, a.node, response);
13499         if(typeof a.callback == "function"){
13500             a.callback(this, a.node);
13501         }
13502     }
13503 });/*
13504  * Based on:
13505  * Ext JS Library 1.1.1
13506  * Copyright(c) 2006-2007, Ext JS, LLC.
13507  *
13508  * Originally Released Under LGPL - original licence link has changed is not relivant.
13509  *
13510  * Fork - LGPL
13511  * <script type="text/javascript">
13512  */
13513
13514 /**
13515 * @class Roo.tree.TreeFilter
13516 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13517 * @param {TreePanel} tree
13518 * @param {Object} config (optional)
13519  */
13520 Roo.tree.TreeFilter = function(tree, config){
13521     this.tree = tree;
13522     this.filtered = {};
13523     Roo.apply(this, config);
13524 };
13525
13526 Roo.tree.TreeFilter.prototype = {
13527     clearBlank:false,
13528     reverse:false,
13529     autoClear:false,
13530     remove:false,
13531
13532      /**
13533      * Filter the data by a specific attribute.
13534      * @param {String/RegExp} value Either string that the attribute value
13535      * should start with or a RegExp to test against the attribute
13536      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13537      * @param {TreeNode} startNode (optional) The node to start the filter at.
13538      */
13539     filter : function(value, attr, startNode){
13540         attr = attr || "text";
13541         var f;
13542         if(typeof value == "string"){
13543             var vlen = value.length;
13544             // auto clear empty filter
13545             if(vlen == 0 && this.clearBlank){
13546                 this.clear();
13547                 return;
13548             }
13549             value = value.toLowerCase();
13550             f = function(n){
13551                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13552             };
13553         }else if(value.exec){ // regex?
13554             f = function(n){
13555                 return value.test(n.attributes[attr]);
13556             };
13557         }else{
13558             throw 'Illegal filter type, must be string or regex';
13559         }
13560         this.filterBy(f, null, startNode);
13561         },
13562
13563     /**
13564      * Filter by a function. The passed function will be called with each
13565      * node in the tree (or from the startNode). If the function returns true, the node is kept
13566      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13567      * @param {Function} fn The filter function
13568      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13569      */
13570     filterBy : function(fn, scope, startNode){
13571         startNode = startNode || this.tree.root;
13572         if(this.autoClear){
13573             this.clear();
13574         }
13575         var af = this.filtered, rv = this.reverse;
13576         var f = function(n){
13577             if(n == startNode){
13578                 return true;
13579             }
13580             if(af[n.id]){
13581                 return false;
13582             }
13583             var m = fn.call(scope || n, n);
13584             if(!m || rv){
13585                 af[n.id] = n;
13586                 n.ui.hide();
13587                 return false;
13588             }
13589             return true;
13590         };
13591         startNode.cascade(f);
13592         if(this.remove){
13593            for(var id in af){
13594                if(typeof id != "function"){
13595                    var n = af[id];
13596                    if(n && n.parentNode){
13597                        n.parentNode.removeChild(n);
13598                    }
13599                }
13600            }
13601         }
13602     },
13603
13604     /**
13605      * Clears the current filter. Note: with the "remove" option
13606      * set a filter cannot be cleared.
13607      */
13608     clear : function(){
13609         var t = this.tree;
13610         var af = this.filtered;
13611         for(var id in af){
13612             if(typeof id != "function"){
13613                 var n = af[id];
13614                 if(n){
13615                     n.ui.show();
13616                 }
13617             }
13618         }
13619         this.filtered = {};
13620     }
13621 };
13622 /*
13623  * Based on:
13624  * Ext JS Library 1.1.1
13625  * Copyright(c) 2006-2007, Ext JS, LLC.
13626  *
13627  * Originally Released Under LGPL - original licence link has changed is not relivant.
13628  *
13629  * Fork - LGPL
13630  * <script type="text/javascript">
13631  */
13632  
13633
13634 /**
13635  * @class Roo.tree.TreeSorter
13636  * Provides sorting of nodes in a TreePanel
13637  * 
13638  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13639  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13640  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13641  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13642  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13643  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13644  * @constructor
13645  * @param {TreePanel} tree
13646  * @param {Object} config
13647  */
13648 Roo.tree.TreeSorter = function(tree, config){
13649     Roo.apply(this, config);
13650     tree.on("beforechildrenrendered", this.doSort, this);
13651     tree.on("append", this.updateSort, this);
13652     tree.on("insert", this.updateSort, this);
13653     
13654     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13655     var p = this.property || "text";
13656     var sortType = this.sortType;
13657     var fs = this.folderSort;
13658     var cs = this.caseSensitive === true;
13659     var leafAttr = this.leafAttr || 'leaf';
13660
13661     this.sortFn = function(n1, n2){
13662         if(fs){
13663             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13664                 return 1;
13665             }
13666             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13667                 return -1;
13668             }
13669         }
13670         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13671         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13672         if(v1 < v2){
13673                         return dsc ? +1 : -1;
13674                 }else if(v1 > v2){
13675                         return dsc ? -1 : +1;
13676         }else{
13677                 return 0;
13678         }
13679     };
13680 };
13681
13682 Roo.tree.TreeSorter.prototype = {
13683     doSort : function(node){
13684         node.sort(this.sortFn);
13685     },
13686     
13687     compareNodes : function(n1, n2){
13688         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13689     },
13690     
13691     updateSort : function(tree, node){
13692         if(node.childrenRendered){
13693             this.doSort.defer(1, this, [node]);
13694         }
13695     }
13696 };/*
13697  * Based on:
13698  * Ext JS Library 1.1.1
13699  * Copyright(c) 2006-2007, Ext JS, LLC.
13700  *
13701  * Originally Released Under LGPL - original licence link has changed is not relivant.
13702  *
13703  * Fork - LGPL
13704  * <script type="text/javascript">
13705  */
13706
13707 if(Roo.dd.DropZone){
13708     
13709 Roo.tree.TreeDropZone = function(tree, config){
13710     this.allowParentInsert = false;
13711     this.allowContainerDrop = false;
13712     this.appendOnly = false;
13713     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13714     this.tree = tree;
13715     this.lastInsertClass = "x-tree-no-status";
13716     this.dragOverData = {};
13717 };
13718
13719 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13720     ddGroup : "TreeDD",
13721     scroll:  true,
13722     
13723     expandDelay : 1000,
13724     
13725     expandNode : function(node){
13726         if(node.hasChildNodes() && !node.isExpanded()){
13727             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13728         }
13729     },
13730     
13731     queueExpand : function(node){
13732         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13733     },
13734     
13735     cancelExpand : function(){
13736         if(this.expandProcId){
13737             clearTimeout(this.expandProcId);
13738             this.expandProcId = false;
13739         }
13740     },
13741     
13742     isValidDropPoint : function(n, pt, dd, e, data){
13743         if(!n || !data){ return false; }
13744         var targetNode = n.node;
13745         var dropNode = data.node;
13746         // default drop rules
13747         if(!(targetNode && targetNode.isTarget && pt)){
13748             return false;
13749         }
13750         if(pt == "append" && targetNode.allowChildren === false){
13751             return false;
13752         }
13753         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13754             return false;
13755         }
13756         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13757             return false;
13758         }
13759         // reuse the object
13760         var overEvent = this.dragOverData;
13761         overEvent.tree = this.tree;
13762         overEvent.target = targetNode;
13763         overEvent.data = data;
13764         overEvent.point = pt;
13765         overEvent.source = dd;
13766         overEvent.rawEvent = e;
13767         overEvent.dropNode = dropNode;
13768         overEvent.cancel = false;  
13769         var result = this.tree.fireEvent("nodedragover", overEvent);
13770         return overEvent.cancel === false && result !== false;
13771     },
13772     
13773     getDropPoint : function(e, n, dd)
13774     {
13775         var tn = n.node;
13776         if(tn.isRoot){
13777             return tn.allowChildren !== false ? "append" : false; // always append for root
13778         }
13779         var dragEl = n.ddel;
13780         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13781         var y = Roo.lib.Event.getPageY(e);
13782         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13783         
13784         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13785         var noAppend = tn.allowChildren === false;
13786         if(this.appendOnly || tn.parentNode.allowChildren === false){
13787             return noAppend ? false : "append";
13788         }
13789         var noBelow = false;
13790         if(!this.allowParentInsert){
13791             noBelow = tn.hasChildNodes() && tn.isExpanded();
13792         }
13793         var q = (b - t) / (noAppend ? 2 : 3);
13794         if(y >= t && y < (t + q)){
13795             return "above";
13796         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13797             return "below";
13798         }else{
13799             return "append";
13800         }
13801     },
13802     
13803     onNodeEnter : function(n, dd, e, data)
13804     {
13805         this.cancelExpand();
13806     },
13807     
13808     onNodeOver : function(n, dd, e, data)
13809     {
13810        
13811         var pt = this.getDropPoint(e, n, dd);
13812         var node = n.node;
13813         
13814         // auto node expand check
13815         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13816             this.queueExpand(node);
13817         }else if(pt != "append"){
13818             this.cancelExpand();
13819         }
13820         
13821         // set the insert point style on the target node
13822         var returnCls = this.dropNotAllowed;
13823         if(this.isValidDropPoint(n, pt, dd, e, data)){
13824            if(pt){
13825                var el = n.ddel;
13826                var cls;
13827                if(pt == "above"){
13828                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13829                    cls = "x-tree-drag-insert-above";
13830                }else if(pt == "below"){
13831                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13832                    cls = "x-tree-drag-insert-below";
13833                }else{
13834                    returnCls = "x-tree-drop-ok-append";
13835                    cls = "x-tree-drag-append";
13836                }
13837                if(this.lastInsertClass != cls){
13838                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13839                    this.lastInsertClass = cls;
13840                }
13841            }
13842        }
13843        return returnCls;
13844     },
13845     
13846     onNodeOut : function(n, dd, e, data){
13847         
13848         this.cancelExpand();
13849         this.removeDropIndicators(n);
13850     },
13851     
13852     onNodeDrop : function(n, dd, e, data){
13853         var point = this.getDropPoint(e, n, dd);
13854         var targetNode = n.node;
13855         targetNode.ui.startDrop();
13856         if(!this.isValidDropPoint(n, point, dd, e, data)){
13857             targetNode.ui.endDrop();
13858             return false;
13859         }
13860         // first try to find the drop node
13861         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13862         var dropEvent = {
13863             tree : this.tree,
13864             target: targetNode,
13865             data: data,
13866             point: point,
13867             source: dd,
13868             rawEvent: e,
13869             dropNode: dropNode,
13870             cancel: !dropNode   
13871         };
13872         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13873         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13874             targetNode.ui.endDrop();
13875             return false;
13876         }
13877         // allow target changing
13878         targetNode = dropEvent.target;
13879         if(point == "append" && !targetNode.isExpanded()){
13880             targetNode.expand(false, null, function(){
13881                 this.completeDrop(dropEvent);
13882             }.createDelegate(this));
13883         }else{
13884             this.completeDrop(dropEvent);
13885         }
13886         return true;
13887     },
13888     
13889     completeDrop : function(de){
13890         var ns = de.dropNode, p = de.point, t = de.target;
13891         if(!(ns instanceof Array)){
13892             ns = [ns];
13893         }
13894         var n;
13895         for(var i = 0, len = ns.length; i < len; i++){
13896             n = ns[i];
13897             if(p == "above"){
13898                 t.parentNode.insertBefore(n, t);
13899             }else if(p == "below"){
13900                 t.parentNode.insertBefore(n, t.nextSibling);
13901             }else{
13902                 t.appendChild(n);
13903             }
13904         }
13905         n.ui.focus();
13906         if(this.tree.hlDrop){
13907             n.ui.highlight();
13908         }
13909         t.ui.endDrop();
13910         this.tree.fireEvent("nodedrop", de);
13911     },
13912     
13913     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13914         if(this.tree.hlDrop){
13915             dropNode.ui.focus();
13916             dropNode.ui.highlight();
13917         }
13918         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13919     },
13920     
13921     getTree : function(){
13922         return this.tree;
13923     },
13924     
13925     removeDropIndicators : function(n){
13926         if(n && n.ddel){
13927             var el = n.ddel;
13928             Roo.fly(el).removeClass([
13929                     "x-tree-drag-insert-above",
13930                     "x-tree-drag-insert-below",
13931                     "x-tree-drag-append"]);
13932             this.lastInsertClass = "_noclass";
13933         }
13934     },
13935     
13936     beforeDragDrop : function(target, e, id){
13937         this.cancelExpand();
13938         return true;
13939     },
13940     
13941     afterRepair : function(data){
13942         if(data && Roo.enableFx){
13943             data.node.ui.highlight();
13944         }
13945         this.hideProxy();
13946     } 
13947     
13948 });
13949
13950 }
13951 /*
13952  * Based on:
13953  * Ext JS Library 1.1.1
13954  * Copyright(c) 2006-2007, Ext JS, LLC.
13955  *
13956  * Originally Released Under LGPL - original licence link has changed is not relivant.
13957  *
13958  * Fork - LGPL
13959  * <script type="text/javascript">
13960  */
13961  
13962
13963 if(Roo.dd.DragZone){
13964 Roo.tree.TreeDragZone = function(tree, config){
13965     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13966     this.tree = tree;
13967 };
13968
13969 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13970     ddGroup : "TreeDD",
13971    
13972     onBeforeDrag : function(data, e){
13973         var n = data.node;
13974         return n && n.draggable && !n.disabled;
13975     },
13976      
13977     
13978     onInitDrag : function(e){
13979         var data = this.dragData;
13980         this.tree.getSelectionModel().select(data.node);
13981         this.proxy.update("");
13982         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13983         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13984     },
13985     
13986     getRepairXY : function(e, data){
13987         return data.node.ui.getDDRepairXY();
13988     },
13989     
13990     onEndDrag : function(data, e){
13991         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13992         
13993         
13994     },
13995     
13996     onValidDrop : function(dd, e, id){
13997         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13998         this.hideProxy();
13999     },
14000     
14001     beforeInvalidDrop : function(e, id){
14002         // this scrolls the original position back into view
14003         var sm = this.tree.getSelectionModel();
14004         sm.clearSelections();
14005         sm.select(this.dragData.node);
14006     }
14007 });
14008 }/*
14009  * Based on:
14010  * Ext JS Library 1.1.1
14011  * Copyright(c) 2006-2007, Ext JS, LLC.
14012  *
14013  * Originally Released Under LGPL - original licence link has changed is not relivant.
14014  *
14015  * Fork - LGPL
14016  * <script type="text/javascript">
14017  */
14018 /**
14019  * @class Roo.tree.TreeEditor
14020  * @extends Roo.Editor
14021  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14022  * as the editor field.
14023  * @constructor
14024  * @param {Object} config (used to be the tree panel.)
14025  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14026  * 
14027  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14028  * @cfg {Roo.form.TextField} field [required] The field configuration
14029  *
14030  * 
14031  */
14032 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14033     var tree = config;
14034     var field;
14035     if (oldconfig) { // old style..
14036         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14037     } else {
14038         // new style..
14039         tree = config.tree;
14040         config.field = config.field  || {};
14041         config.field.xtype = 'TextField';
14042         field = Roo.factory(config.field, Roo.form);
14043     }
14044     config = config || {};
14045     
14046     
14047     this.addEvents({
14048         /**
14049          * @event beforenodeedit
14050          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14051          * false from the handler of this event.
14052          * @param {Editor} this
14053          * @param {Roo.tree.Node} node 
14054          */
14055         "beforenodeedit" : true
14056     });
14057     
14058     //Roo.log(config);
14059     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14060
14061     this.tree = tree;
14062
14063     tree.on('beforeclick', this.beforeNodeClick, this);
14064     tree.getTreeEl().on('mousedown', this.hide, this);
14065     this.on('complete', this.updateNode, this);
14066     this.on('beforestartedit', this.fitToTree, this);
14067     this.on('startedit', this.bindScroll, this, {delay:10});
14068     this.on('specialkey', this.onSpecialKey, this);
14069 };
14070
14071 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14072     /**
14073      * @cfg {String} alignment
14074      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14075      */
14076     alignment: "l-l",
14077     // inherit
14078     autoSize: false,
14079     /**
14080      * @cfg {Boolean} hideEl
14081      * True to hide the bound element while the editor is displayed (defaults to false)
14082      */
14083     hideEl : false,
14084     /**
14085      * @cfg {String} cls
14086      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14087      */
14088     cls: "x-small-editor x-tree-editor",
14089     /**
14090      * @cfg {Boolean} shim
14091      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14092      */
14093     shim:false,
14094     // inherit
14095     shadow:"frame",
14096     /**
14097      * @cfg {Number} maxWidth
14098      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14099      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14100      * scroll and client offsets into account prior to each edit.
14101      */
14102     maxWidth: 250,
14103
14104     editDelay : 350,
14105
14106     // private
14107     fitToTree : function(ed, el){
14108         var td = this.tree.getTreeEl().dom, nd = el.dom;
14109         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14110             td.scrollLeft = nd.offsetLeft;
14111         }
14112         var w = Math.min(
14113                 this.maxWidth,
14114                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14115         this.setSize(w, '');
14116         
14117         return this.fireEvent('beforenodeedit', this, this.editNode);
14118         
14119     },
14120
14121     // private
14122     triggerEdit : function(node){
14123         this.completeEdit();
14124         this.editNode = node;
14125         this.startEdit(node.ui.textNode, node.text);
14126     },
14127
14128     // private
14129     bindScroll : function(){
14130         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14131     },
14132
14133     // private
14134     beforeNodeClick : function(node, e){
14135         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14136         this.lastClick = new Date();
14137         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14138             e.stopEvent();
14139             this.triggerEdit(node);
14140             return false;
14141         }
14142         return true;
14143     },
14144
14145     // private
14146     updateNode : function(ed, value){
14147         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14148         this.editNode.setText(value);
14149     },
14150
14151     // private
14152     onHide : function(){
14153         Roo.tree.TreeEditor.superclass.onHide.call(this);
14154         if(this.editNode){
14155             this.editNode.ui.focus();
14156         }
14157     },
14158
14159     // private
14160     onSpecialKey : function(field, e){
14161         var k = e.getKey();
14162         if(k == e.ESC){
14163             e.stopEvent();
14164             this.cancelEdit();
14165         }else if(k == e.ENTER && !e.hasModifier()){
14166             e.stopEvent();
14167             this.completeEdit();
14168         }
14169     }
14170 });//<Script type="text/javascript">
14171 /*
14172  * Based on:
14173  * Ext JS Library 1.1.1
14174  * Copyright(c) 2006-2007, Ext JS, LLC.
14175  *
14176  * Originally Released Under LGPL - original licence link has changed is not relivant.
14177  *
14178  * Fork - LGPL
14179  * <script type="text/javascript">
14180  */
14181  
14182 /**
14183  * Not documented??? - probably should be...
14184  */
14185
14186 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14187     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14188     
14189     renderElements : function(n, a, targetNode, bulkRender){
14190         //consel.log("renderElements?");
14191         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14192
14193         var t = n.getOwnerTree();
14194         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14195         
14196         var cols = t.columns;
14197         var bw = t.borderWidth;
14198         var c = cols[0];
14199         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14200          var cb = typeof a.checked == "boolean";
14201         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14202         var colcls = 'x-t-' + tid + '-c0';
14203         var buf = [
14204             '<li class="x-tree-node">',
14205             
14206                 
14207                 '<div class="x-tree-node-el ', a.cls,'">',
14208                     // extran...
14209                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14210                 
14211                 
14212                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14213                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14214                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14215                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14216                            (a.iconCls ? ' '+a.iconCls : ''),
14217                            '" unselectable="on" />',
14218                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14219                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14220                              
14221                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14222                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14223                             '<span unselectable="on" qtip="' + tx + '">',
14224                              tx,
14225                              '</span></a>' ,
14226                     '</div>',
14227                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14228                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14229                  ];
14230         for(var i = 1, len = cols.length; i < len; i++){
14231             c = cols[i];
14232             colcls = 'x-t-' + tid + '-c' +i;
14233             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14234             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14235                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14236                       "</div>");
14237          }
14238          
14239          buf.push(
14240             '</a>',
14241             '<div class="x-clear"></div></div>',
14242             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14243             "</li>");
14244         
14245         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14246             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14247                                 n.nextSibling.ui.getEl(), buf.join(""));
14248         }else{
14249             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14250         }
14251         var el = this.wrap.firstChild;
14252         this.elRow = el;
14253         this.elNode = el.firstChild;
14254         this.ranchor = el.childNodes[1];
14255         this.ctNode = this.wrap.childNodes[1];
14256         var cs = el.firstChild.childNodes;
14257         this.indentNode = cs[0];
14258         this.ecNode = cs[1];
14259         this.iconNode = cs[2];
14260         var index = 3;
14261         if(cb){
14262             this.checkbox = cs[3];
14263             index++;
14264         }
14265         this.anchor = cs[index];
14266         
14267         this.textNode = cs[index].firstChild;
14268         
14269         //el.on("click", this.onClick, this);
14270         //el.on("dblclick", this.onDblClick, this);
14271         
14272         
14273        // console.log(this);
14274     },
14275     initEvents : function(){
14276         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14277         
14278             
14279         var a = this.ranchor;
14280
14281         var el = Roo.get(a);
14282
14283         if(Roo.isOpera){ // opera render bug ignores the CSS
14284             el.setStyle("text-decoration", "none");
14285         }
14286
14287         el.on("click", this.onClick, this);
14288         el.on("dblclick", this.onDblClick, this);
14289         el.on("contextmenu", this.onContextMenu, this);
14290         
14291     },
14292     
14293     /*onSelectedChange : function(state){
14294         if(state){
14295             this.focus();
14296             this.addClass("x-tree-selected");
14297         }else{
14298             //this.blur();
14299             this.removeClass("x-tree-selected");
14300         }
14301     },*/
14302     addClass : function(cls){
14303         if(this.elRow){
14304             Roo.fly(this.elRow).addClass(cls);
14305         }
14306         
14307     },
14308     
14309     
14310     removeClass : function(cls){
14311         if(this.elRow){
14312             Roo.fly(this.elRow).removeClass(cls);
14313         }
14314     }
14315
14316     
14317     
14318 });//<Script type="text/javascript">
14319
14320 /*
14321  * Based on:
14322  * Ext JS Library 1.1.1
14323  * Copyright(c) 2006-2007, Ext JS, LLC.
14324  *
14325  * Originally Released Under LGPL - original licence link has changed is not relivant.
14326  *
14327  * Fork - LGPL
14328  * <script type="text/javascript">
14329  */
14330  
14331
14332 /**
14333  * @class Roo.tree.ColumnTree
14334  * @extends Roo.tree.TreePanel
14335  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14336  * @cfg {int} borderWidth  compined right/left border allowance
14337  * @constructor
14338  * @param {String/HTMLElement/Element} el The container element
14339  * @param {Object} config
14340  */
14341 Roo.tree.ColumnTree =  function(el, config)
14342 {
14343    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14344    this.addEvents({
14345         /**
14346         * @event resize
14347         * Fire this event on a container when it resizes
14348         * @param {int} w Width
14349         * @param {int} h Height
14350         */
14351        "resize" : true
14352     });
14353     this.on('resize', this.onResize, this);
14354 };
14355
14356 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14357     //lines:false,
14358     
14359     
14360     borderWidth: Roo.isBorderBox ? 0 : 2, 
14361     headEls : false,
14362     
14363     render : function(){
14364         // add the header.....
14365        
14366         Roo.tree.ColumnTree.superclass.render.apply(this);
14367         
14368         this.el.addClass('x-column-tree');
14369         
14370         this.headers = this.el.createChild(
14371             {cls:'x-tree-headers'},this.innerCt.dom);
14372    
14373         var cols = this.columns, c;
14374         var totalWidth = 0;
14375         this.headEls = [];
14376         var  len = cols.length;
14377         for(var i = 0; i < len; i++){
14378              c = cols[i];
14379              totalWidth += c.width;
14380             this.headEls.push(this.headers.createChild({
14381                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14382                  cn: {
14383                      cls:'x-tree-hd-text',
14384                      html: c.header
14385                  },
14386                  style:'width:'+(c.width-this.borderWidth)+'px;'
14387              }));
14388         }
14389         this.headers.createChild({cls:'x-clear'});
14390         // prevent floats from wrapping when clipped
14391         this.headers.setWidth(totalWidth);
14392         //this.innerCt.setWidth(totalWidth);
14393         this.innerCt.setStyle({ overflow: 'auto' });
14394         this.onResize(this.width, this.height);
14395              
14396         
14397     },
14398     onResize : function(w,h)
14399     {
14400         this.height = h;
14401         this.width = w;
14402         // resize cols..
14403         this.innerCt.setWidth(this.width);
14404         this.innerCt.setHeight(this.height-20);
14405         
14406         // headers...
14407         var cols = this.columns, c;
14408         var totalWidth = 0;
14409         var expEl = false;
14410         var len = cols.length;
14411         for(var i = 0; i < len; i++){
14412             c = cols[i];
14413             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14414                 // it's the expander..
14415                 expEl  = this.headEls[i];
14416                 continue;
14417             }
14418             totalWidth += c.width;
14419             
14420         }
14421         if (expEl) {
14422             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14423         }
14424         this.headers.setWidth(w-20);
14425
14426         
14427         
14428         
14429     }
14430 });
14431 /*
14432  * Based on:
14433  * Ext JS Library 1.1.1
14434  * Copyright(c) 2006-2007, Ext JS, LLC.
14435  *
14436  * Originally Released Under LGPL - original licence link has changed is not relivant.
14437  *
14438  * Fork - LGPL
14439  * <script type="text/javascript">
14440  */
14441  
14442 /**
14443  * @class Roo.menu.Menu
14444  * @extends Roo.util.Observable
14445  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14446  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14447  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14448  * @constructor
14449  * Creates a new Menu
14450  * @param {Object} config Configuration options
14451  */
14452 Roo.menu.Menu = function(config){
14453     
14454     Roo.menu.Menu.superclass.constructor.call(this, config);
14455     
14456     this.id = this.id || Roo.id();
14457     this.addEvents({
14458         /**
14459          * @event beforeshow
14460          * Fires before this menu is displayed
14461          * @param {Roo.menu.Menu} this
14462          */
14463         beforeshow : true,
14464         /**
14465          * @event beforehide
14466          * Fires before this menu is hidden
14467          * @param {Roo.menu.Menu} this
14468          */
14469         beforehide : true,
14470         /**
14471          * @event show
14472          * Fires after this menu is displayed
14473          * @param {Roo.menu.Menu} this
14474          */
14475         show : true,
14476         /**
14477          * @event hide
14478          * Fires after this menu is hidden
14479          * @param {Roo.menu.Menu} this
14480          */
14481         hide : true,
14482         /**
14483          * @event click
14484          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14485          * @param {Roo.menu.Menu} this
14486          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14487          * @param {Roo.EventObject} e
14488          */
14489         click : true,
14490         /**
14491          * @event mouseover
14492          * Fires when the mouse is hovering over this menu
14493          * @param {Roo.menu.Menu} this
14494          * @param {Roo.EventObject} e
14495          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14496          */
14497         mouseover : true,
14498         /**
14499          * @event mouseout
14500          * Fires when the mouse exits this menu
14501          * @param {Roo.menu.Menu} this
14502          * @param {Roo.EventObject} e
14503          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14504          */
14505         mouseout : true,
14506         /**
14507          * @event itemclick
14508          * Fires when a menu item contained in this menu is clicked
14509          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14510          * @param {Roo.EventObject} e
14511          */
14512         itemclick: true
14513     });
14514     if (this.registerMenu) {
14515         Roo.menu.MenuMgr.register(this);
14516     }
14517     
14518     var mis = this.items;
14519     this.items = new Roo.util.MixedCollection();
14520     if(mis){
14521         this.add.apply(this, mis);
14522     }
14523 };
14524
14525 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14526     /**
14527      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14528      */
14529     minWidth : 120,
14530     /**
14531      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14532      * for bottom-right shadow (defaults to "sides")
14533      */
14534     shadow : "sides",
14535     /**
14536      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14537      * this menu (defaults to "tl-tr?")
14538      */
14539     subMenuAlign : "tl-tr?",
14540     /**
14541      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14542      * relative to its element of origin (defaults to "tl-bl?")
14543      */
14544     defaultAlign : "tl-bl?",
14545     /**
14546      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14547      */
14548     allowOtherMenus : false,
14549     /**
14550      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14551      */
14552     registerMenu : true,
14553
14554     hidden:true,
14555
14556     // private
14557     render : function(){
14558         if(this.el){
14559             return;
14560         }
14561         var el = this.el = new Roo.Layer({
14562             cls: "x-menu",
14563             shadow:this.shadow,
14564             constrain: false,
14565             parentEl: this.parentEl || document.body,
14566             zindex:15000
14567         });
14568
14569         this.keyNav = new Roo.menu.MenuNav(this);
14570
14571         if(this.plain){
14572             el.addClass("x-menu-plain");
14573         }
14574         if(this.cls){
14575             el.addClass(this.cls);
14576         }
14577         // generic focus element
14578         this.focusEl = el.createChild({
14579             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14580         });
14581         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14582         //disabling touch- as it's causing issues ..
14583         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14584         ul.on('click'   , this.onClick, this);
14585         
14586         
14587         ul.on("mouseover", this.onMouseOver, this);
14588         ul.on("mouseout", this.onMouseOut, this);
14589         this.items.each(function(item){
14590             if (item.hidden) {
14591                 return;
14592             }
14593             
14594             var li = document.createElement("li");
14595             li.className = "x-menu-list-item";
14596             ul.dom.appendChild(li);
14597             item.render(li, this);
14598         }, this);
14599         this.ul = ul;
14600         this.autoWidth();
14601     },
14602
14603     // private
14604     autoWidth : function(){
14605         var el = this.el, ul = this.ul;
14606         if(!el){
14607             return;
14608         }
14609         var w = this.width;
14610         if(w){
14611             el.setWidth(w);
14612         }else if(Roo.isIE){
14613             el.setWidth(this.minWidth);
14614             var t = el.dom.offsetWidth; // force recalc
14615             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14616         }
14617     },
14618
14619     // private
14620     delayAutoWidth : function(){
14621         if(this.rendered){
14622             if(!this.awTask){
14623                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14624             }
14625             this.awTask.delay(20);
14626         }
14627     },
14628
14629     // private
14630     findTargetItem : function(e){
14631         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14632         if(t && t.menuItemId){
14633             return this.items.get(t.menuItemId);
14634         }
14635     },
14636
14637     // private
14638     onClick : function(e){
14639         Roo.log("menu.onClick");
14640         var t = this.findTargetItem(e);
14641         if(!t){
14642             return;
14643         }
14644         Roo.log(e);
14645         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14646             if(t == this.activeItem && t.shouldDeactivate(e)){
14647                 this.activeItem.deactivate();
14648                 delete this.activeItem;
14649                 return;
14650             }
14651             if(t.canActivate){
14652                 this.setActiveItem(t, true);
14653             }
14654             return;
14655             
14656             
14657         }
14658         
14659         t.onClick(e);
14660         this.fireEvent("click", this, t, e);
14661     },
14662
14663     // private
14664     setActiveItem : function(item, autoExpand){
14665         if(item != this.activeItem){
14666             if(this.activeItem){
14667                 this.activeItem.deactivate();
14668             }
14669             this.activeItem = item;
14670             item.activate(autoExpand);
14671         }else if(autoExpand){
14672             item.expandMenu();
14673         }
14674     },
14675
14676     // private
14677     tryActivate : function(start, step){
14678         var items = this.items;
14679         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14680             var item = items.get(i);
14681             if(!item.disabled && item.canActivate){
14682                 this.setActiveItem(item, false);
14683                 return item;
14684             }
14685         }
14686         return false;
14687     },
14688
14689     // private
14690     onMouseOver : function(e){
14691         var t;
14692         if(t = this.findTargetItem(e)){
14693             if(t.canActivate && !t.disabled){
14694                 this.setActiveItem(t, true);
14695             }
14696         }
14697         this.fireEvent("mouseover", this, e, t);
14698     },
14699
14700     // private
14701     onMouseOut : function(e){
14702         var t;
14703         if(t = this.findTargetItem(e)){
14704             if(t == this.activeItem && t.shouldDeactivate(e)){
14705                 this.activeItem.deactivate();
14706                 delete this.activeItem;
14707             }
14708         }
14709         this.fireEvent("mouseout", this, e, t);
14710     },
14711
14712     /**
14713      * Read-only.  Returns true if the menu is currently displayed, else false.
14714      * @type Boolean
14715      */
14716     isVisible : function(){
14717         return this.el && !this.hidden;
14718     },
14719
14720     /**
14721      * Displays this menu relative to another element
14722      * @param {String/HTMLElement/Roo.Element} element The element to align to
14723      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14724      * the element (defaults to this.defaultAlign)
14725      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14726      */
14727     show : function(el, pos, parentMenu){
14728         this.parentMenu = parentMenu;
14729         if(!this.el){
14730             this.render();
14731         }
14732         this.fireEvent("beforeshow", this);
14733         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14734     },
14735
14736     /**
14737      * Displays this menu at a specific xy position
14738      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14739      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14740      */
14741     showAt : function(xy, parentMenu, /* private: */_e){
14742         this.parentMenu = parentMenu;
14743         if(!this.el){
14744             this.render();
14745         }
14746         if(_e !== false){
14747             this.fireEvent("beforeshow", this);
14748             xy = this.el.adjustForConstraints(xy);
14749         }
14750         this.el.setXY(xy);
14751         this.el.show();
14752         this.hidden = false;
14753         this.focus();
14754         this.fireEvent("show", this);
14755     },
14756
14757     focus : function(){
14758         if(!this.hidden){
14759             this.doFocus.defer(50, this);
14760         }
14761     },
14762
14763     doFocus : function(){
14764         if(!this.hidden){
14765             this.focusEl.focus();
14766         }
14767     },
14768
14769     /**
14770      * Hides this menu and optionally all parent menus
14771      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14772      */
14773     hide : function(deep){
14774         if(this.el && this.isVisible()){
14775             this.fireEvent("beforehide", this);
14776             if(this.activeItem){
14777                 this.activeItem.deactivate();
14778                 this.activeItem = null;
14779             }
14780             this.el.hide();
14781             this.hidden = true;
14782             this.fireEvent("hide", this);
14783         }
14784         if(deep === true && this.parentMenu){
14785             this.parentMenu.hide(true);
14786         }
14787     },
14788
14789     /**
14790      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14791      * Any of the following are valid:
14792      * <ul>
14793      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14794      * <li>An HTMLElement object which will be converted to a menu item</li>
14795      * <li>A menu item config object that will be created as a new menu item</li>
14796      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14797      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14798      * </ul>
14799      * Usage:
14800      * <pre><code>
14801 // Create the menu
14802 var menu = new Roo.menu.Menu();
14803
14804 // Create a menu item to add by reference
14805 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14806
14807 // Add a bunch of items at once using different methods.
14808 // Only the last item added will be returned.
14809 var item = menu.add(
14810     menuItem,                // add existing item by ref
14811     'Dynamic Item',          // new TextItem
14812     '-',                     // new separator
14813     { text: 'Config Item' }  // new item by config
14814 );
14815 </code></pre>
14816      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14817      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14818      */
14819     add : function(){
14820         var a = arguments, l = a.length, item;
14821         for(var i = 0; i < l; i++){
14822             var el = a[i];
14823             if ((typeof(el) == "object") && el.xtype && el.xns) {
14824                 el = Roo.factory(el, Roo.menu);
14825             }
14826             
14827             if(el.render){ // some kind of Item
14828                 item = this.addItem(el);
14829             }else if(typeof el == "string"){ // string
14830                 if(el == "separator" || el == "-"){
14831                     item = this.addSeparator();
14832                 }else{
14833                     item = this.addText(el);
14834                 }
14835             }else if(el.tagName || el.el){ // element
14836                 item = this.addElement(el);
14837             }else if(typeof el == "object"){ // must be menu item config?
14838                 item = this.addMenuItem(el);
14839             }
14840         }
14841         return item;
14842     },
14843
14844     /**
14845      * Returns this menu's underlying {@link Roo.Element} object
14846      * @return {Roo.Element} The element
14847      */
14848     getEl : function(){
14849         if(!this.el){
14850             this.render();
14851         }
14852         return this.el;
14853     },
14854
14855     /**
14856      * Adds a separator bar to the menu
14857      * @return {Roo.menu.Item} The menu item that was added
14858      */
14859     addSeparator : function(){
14860         return this.addItem(new Roo.menu.Separator());
14861     },
14862
14863     /**
14864      * Adds an {@link Roo.Element} object to the menu
14865      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14866      * @return {Roo.menu.Item} The menu item that was added
14867      */
14868     addElement : function(el){
14869         return this.addItem(new Roo.menu.BaseItem(el));
14870     },
14871
14872     /**
14873      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14874      * @param {Roo.menu.Item} item The menu item to add
14875      * @return {Roo.menu.Item} The menu item that was added
14876      */
14877     addItem : function(item){
14878         this.items.add(item);
14879         if(this.ul){
14880             var li = document.createElement("li");
14881             li.className = "x-menu-list-item";
14882             this.ul.dom.appendChild(li);
14883             item.render(li, this);
14884             this.delayAutoWidth();
14885         }
14886         return item;
14887     },
14888
14889     /**
14890      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14891      * @param {Object} config A MenuItem config object
14892      * @return {Roo.menu.Item} The menu item that was added
14893      */
14894     addMenuItem : function(config){
14895         if(!(config instanceof Roo.menu.Item)){
14896             if(typeof config.checked == "boolean"){ // must be check menu item config?
14897                 config = new Roo.menu.CheckItem(config);
14898             }else{
14899                 config = new Roo.menu.Item(config);
14900             }
14901         }
14902         return this.addItem(config);
14903     },
14904
14905     /**
14906      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14907      * @param {String} text The text to display in the menu item
14908      * @return {Roo.menu.Item} The menu item that was added
14909      */
14910     addText : function(text){
14911         return this.addItem(new Roo.menu.TextItem({ text : text }));
14912     },
14913
14914     /**
14915      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14916      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14917      * @param {Roo.menu.Item} item The menu item to add
14918      * @return {Roo.menu.Item} The menu item that was added
14919      */
14920     insert : function(index, item){
14921         this.items.insert(index, item);
14922         if(this.ul){
14923             var li = document.createElement("li");
14924             li.className = "x-menu-list-item";
14925             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14926             item.render(li, this);
14927             this.delayAutoWidth();
14928         }
14929         return item;
14930     },
14931
14932     /**
14933      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14934      * @param {Roo.menu.Item} item The menu item to remove
14935      */
14936     remove : function(item){
14937         this.items.removeKey(item.id);
14938         item.destroy();
14939     },
14940
14941     /**
14942      * Removes and destroys all items in the menu
14943      */
14944     removeAll : function(){
14945         var f;
14946         while(f = this.items.first()){
14947             this.remove(f);
14948         }
14949     }
14950 });
14951
14952 // MenuNav is a private utility class used internally by the Menu
14953 Roo.menu.MenuNav = function(menu){
14954     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14955     this.scope = this.menu = menu;
14956 };
14957
14958 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14959     doRelay : function(e, h){
14960         var k = e.getKey();
14961         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14962             this.menu.tryActivate(0, 1);
14963             return false;
14964         }
14965         return h.call(this.scope || this, e, this.menu);
14966     },
14967
14968     up : function(e, m){
14969         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14970             m.tryActivate(m.items.length-1, -1);
14971         }
14972     },
14973
14974     down : function(e, m){
14975         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14976             m.tryActivate(0, 1);
14977         }
14978     },
14979
14980     right : function(e, m){
14981         if(m.activeItem){
14982             m.activeItem.expandMenu(true);
14983         }
14984     },
14985
14986     left : function(e, m){
14987         m.hide();
14988         if(m.parentMenu && m.parentMenu.activeItem){
14989             m.parentMenu.activeItem.activate();
14990         }
14991     },
14992
14993     enter : function(e, m){
14994         if(m.activeItem){
14995             e.stopPropagation();
14996             m.activeItem.onClick(e);
14997             m.fireEvent("click", this, m.activeItem);
14998             return true;
14999         }
15000     }
15001 });/*
15002  * Based on:
15003  * Ext JS Library 1.1.1
15004  * Copyright(c) 2006-2007, Ext JS, LLC.
15005  *
15006  * Originally Released Under LGPL - original licence link has changed is not relivant.
15007  *
15008  * Fork - LGPL
15009  * <script type="text/javascript">
15010  */
15011  
15012 /**
15013  * @class Roo.menu.MenuMgr
15014  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15015  * @static
15016  */
15017 Roo.menu.MenuMgr = function(){
15018    var menus, active, groups = {}, attached = false, lastShow = new Date();
15019
15020    // private - called when first menu is created
15021    function init(){
15022        menus = {};
15023        active = new Roo.util.MixedCollection();
15024        Roo.get(document).addKeyListener(27, function(){
15025            if(active.length > 0){
15026                hideAll();
15027            }
15028        });
15029    }
15030
15031    // private
15032    function hideAll(){
15033        if(active && active.length > 0){
15034            var c = active.clone();
15035            c.each(function(m){
15036                m.hide();
15037            });
15038        }
15039    }
15040
15041    // private
15042    function onHide(m){
15043        active.remove(m);
15044        if(active.length < 1){
15045            Roo.get(document).un("mousedown", onMouseDown);
15046            attached = false;
15047        }
15048    }
15049
15050    // private
15051    function onShow(m){
15052        var last = active.last();
15053        lastShow = new Date();
15054        active.add(m);
15055        if(!attached){
15056            Roo.get(document).on("mousedown", onMouseDown);
15057            attached = true;
15058        }
15059        if(m.parentMenu){
15060           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15061           m.parentMenu.activeChild = m;
15062        }else if(last && last.isVisible()){
15063           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15064        }
15065    }
15066
15067    // private
15068    function onBeforeHide(m){
15069        if(m.activeChild){
15070            m.activeChild.hide();
15071        }
15072        if(m.autoHideTimer){
15073            clearTimeout(m.autoHideTimer);
15074            delete m.autoHideTimer;
15075        }
15076    }
15077
15078    // private
15079    function onBeforeShow(m){
15080        var pm = m.parentMenu;
15081        if(!pm && !m.allowOtherMenus){
15082            hideAll();
15083        }else if(pm && pm.activeChild && active != m){
15084            pm.activeChild.hide();
15085        }
15086    }
15087
15088    // private
15089    function onMouseDown(e){
15090        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15091            hideAll();
15092        }
15093    }
15094
15095    // private
15096    function onBeforeCheck(mi, state){
15097        if(state){
15098            var g = groups[mi.group];
15099            for(var i = 0, l = g.length; i < l; i++){
15100                if(g[i] != mi){
15101                    g[i].setChecked(false);
15102                }
15103            }
15104        }
15105    }
15106
15107    return {
15108
15109        /**
15110         * Hides all menus that are currently visible
15111         */
15112        hideAll : function(){
15113             hideAll();  
15114        },
15115
15116        // private
15117        register : function(menu){
15118            if(!menus){
15119                init();
15120            }
15121            menus[menu.id] = menu;
15122            menu.on("beforehide", onBeforeHide);
15123            menu.on("hide", onHide);
15124            menu.on("beforeshow", onBeforeShow);
15125            menu.on("show", onShow);
15126            var g = menu.group;
15127            if(g && menu.events["checkchange"]){
15128                if(!groups[g]){
15129                    groups[g] = [];
15130                }
15131                groups[g].push(menu);
15132                menu.on("checkchange", onCheck);
15133            }
15134        },
15135
15136         /**
15137          * Returns a {@link Roo.menu.Menu} object
15138          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15139          * be used to generate and return a new Menu instance.
15140          */
15141        get : function(menu){
15142            if(typeof menu == "string"){ // menu id
15143                return menus[menu];
15144            }else if(menu.events){  // menu instance
15145                return menu;
15146            }else if(typeof menu.length == 'number'){ // array of menu items?
15147                return new Roo.menu.Menu({items:menu});
15148            }else{ // otherwise, must be a config
15149                return new Roo.menu.Menu(menu);
15150            }
15151        },
15152
15153        // private
15154        unregister : function(menu){
15155            delete menus[menu.id];
15156            menu.un("beforehide", onBeforeHide);
15157            menu.un("hide", onHide);
15158            menu.un("beforeshow", onBeforeShow);
15159            menu.un("show", onShow);
15160            var g = menu.group;
15161            if(g && menu.events["checkchange"]){
15162                groups[g].remove(menu);
15163                menu.un("checkchange", onCheck);
15164            }
15165        },
15166
15167        // private
15168        registerCheckable : function(menuItem){
15169            var g = menuItem.group;
15170            if(g){
15171                if(!groups[g]){
15172                    groups[g] = [];
15173                }
15174                groups[g].push(menuItem);
15175                menuItem.on("beforecheckchange", onBeforeCheck);
15176            }
15177        },
15178
15179        // private
15180        unregisterCheckable : function(menuItem){
15181            var g = menuItem.group;
15182            if(g){
15183                groups[g].remove(menuItem);
15184                menuItem.un("beforecheckchange", onBeforeCheck);
15185            }
15186        }
15187    };
15188 }();/*
15189  * Based on:
15190  * Ext JS Library 1.1.1
15191  * Copyright(c) 2006-2007, Ext JS, LLC.
15192  *
15193  * Originally Released Under LGPL - original licence link has changed is not relivant.
15194  *
15195  * Fork - LGPL
15196  * <script type="text/javascript">
15197  */
15198  
15199
15200 /**
15201  * @class Roo.menu.BaseItem
15202  * @extends Roo.Component
15203  * @abstract
15204  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15205  * management and base configuration options shared by all menu components.
15206  * @constructor
15207  * Creates a new BaseItem
15208  * @param {Object} config Configuration options
15209  */
15210 Roo.menu.BaseItem = function(config){
15211     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15212
15213     this.addEvents({
15214         /**
15215          * @event click
15216          * Fires when this item is clicked
15217          * @param {Roo.menu.BaseItem} this
15218          * @param {Roo.EventObject} e
15219          */
15220         click: true,
15221         /**
15222          * @event activate
15223          * Fires when this item is activated
15224          * @param {Roo.menu.BaseItem} this
15225          */
15226         activate : true,
15227         /**
15228          * @event deactivate
15229          * Fires when this item is deactivated
15230          * @param {Roo.menu.BaseItem} this
15231          */
15232         deactivate : true
15233     });
15234
15235     if(this.handler){
15236         this.on("click", this.handler, this.scope, true);
15237     }
15238 };
15239
15240 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15241     /**
15242      * @cfg {Function} handler
15243      * A function that will handle the click event of this menu item (defaults to undefined)
15244      */
15245     /**
15246      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15247      */
15248     canActivate : false,
15249     
15250      /**
15251      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15252      */
15253     hidden: false,
15254     
15255     /**
15256      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15257      */
15258     activeClass : "x-menu-item-active",
15259     /**
15260      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15261      */
15262     hideOnClick : true,
15263     /**
15264      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15265      */
15266     hideDelay : 100,
15267
15268     // private
15269     ctype: "Roo.menu.BaseItem",
15270
15271     // private
15272     actionMode : "container",
15273
15274     // private
15275     render : function(container, parentMenu){
15276         this.parentMenu = parentMenu;
15277         Roo.menu.BaseItem.superclass.render.call(this, container);
15278         this.container.menuItemId = this.id;
15279     },
15280
15281     // private
15282     onRender : function(container, position){
15283         this.el = Roo.get(this.el);
15284         container.dom.appendChild(this.el.dom);
15285     },
15286
15287     // private
15288     onClick : function(e){
15289         if(!this.disabled && this.fireEvent("click", this, e) !== false
15290                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15291             this.handleClick(e);
15292         }else{
15293             e.stopEvent();
15294         }
15295     },
15296
15297     // private
15298     activate : function(){
15299         if(this.disabled){
15300             return false;
15301         }
15302         var li = this.container;
15303         li.addClass(this.activeClass);
15304         this.region = li.getRegion().adjust(2, 2, -2, -2);
15305         this.fireEvent("activate", this);
15306         return true;
15307     },
15308
15309     // private
15310     deactivate : function(){
15311         this.container.removeClass(this.activeClass);
15312         this.fireEvent("deactivate", this);
15313     },
15314
15315     // private
15316     shouldDeactivate : function(e){
15317         return !this.region || !this.region.contains(e.getPoint());
15318     },
15319
15320     // private
15321     handleClick : function(e){
15322         if(this.hideOnClick){
15323             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15324         }
15325     },
15326
15327     // private
15328     expandMenu : function(autoActivate){
15329         // do nothing
15330     },
15331
15332     // private
15333     hideMenu : function(){
15334         // do nothing
15335     }
15336 });/*
15337  * Based on:
15338  * Ext JS Library 1.1.1
15339  * Copyright(c) 2006-2007, Ext JS, LLC.
15340  *
15341  * Originally Released Under LGPL - original licence link has changed is not relivant.
15342  *
15343  * Fork - LGPL
15344  * <script type="text/javascript">
15345  */
15346  
15347 /**
15348  * @class Roo.menu.Adapter
15349  * @extends Roo.menu.BaseItem
15350  * @abstract
15351  * 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.
15352  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15353  * @constructor
15354  * Creates a new Adapter
15355  * @param {Object} config Configuration options
15356  */
15357 Roo.menu.Adapter = function(component, config){
15358     Roo.menu.Adapter.superclass.constructor.call(this, config);
15359     this.component = component;
15360 };
15361 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15362     // private
15363     canActivate : true,
15364
15365     // private
15366     onRender : function(container, position){
15367         this.component.render(container);
15368         this.el = this.component.getEl();
15369     },
15370
15371     // private
15372     activate : function(){
15373         if(this.disabled){
15374             return false;
15375         }
15376         this.component.focus();
15377         this.fireEvent("activate", this);
15378         return true;
15379     },
15380
15381     // private
15382     deactivate : function(){
15383         this.fireEvent("deactivate", this);
15384     },
15385
15386     // private
15387     disable : function(){
15388         this.component.disable();
15389         Roo.menu.Adapter.superclass.disable.call(this);
15390     },
15391
15392     // private
15393     enable : function(){
15394         this.component.enable();
15395         Roo.menu.Adapter.superclass.enable.call(this);
15396     }
15397 });/*
15398  * Based on:
15399  * Ext JS Library 1.1.1
15400  * Copyright(c) 2006-2007, Ext JS, LLC.
15401  *
15402  * Originally Released Under LGPL - original licence link has changed is not relivant.
15403  *
15404  * Fork - LGPL
15405  * <script type="text/javascript">
15406  */
15407
15408 /**
15409  * @class Roo.menu.TextItem
15410  * @extends Roo.menu.BaseItem
15411  * Adds a static text string to a menu, usually used as either a heading or group separator.
15412  * Note: old style constructor with text is still supported.
15413  * 
15414  * @constructor
15415  * Creates a new TextItem
15416  * @param {Object} cfg Configuration
15417  */
15418 Roo.menu.TextItem = function(cfg){
15419     if (typeof(cfg) == 'string') {
15420         this.text = cfg;
15421     } else {
15422         Roo.apply(this,cfg);
15423     }
15424     
15425     Roo.menu.TextItem.superclass.constructor.call(this);
15426 };
15427
15428 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15429     /**
15430      * @cfg {String} text Text to show on item.
15431      */
15432     text : '',
15433     
15434     /**
15435      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15436      */
15437     hideOnClick : false,
15438     /**
15439      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15440      */
15441     itemCls : "x-menu-text",
15442
15443     // private
15444     onRender : function(){
15445         var s = document.createElement("span");
15446         s.className = this.itemCls;
15447         s.innerHTML = this.text;
15448         this.el = s;
15449         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15450     }
15451 });/*
15452  * Based on:
15453  * Ext JS Library 1.1.1
15454  * Copyright(c) 2006-2007, Ext JS, LLC.
15455  *
15456  * Originally Released Under LGPL - original licence link has changed is not relivant.
15457  *
15458  * Fork - LGPL
15459  * <script type="text/javascript">
15460  */
15461
15462 /**
15463  * @class Roo.menu.Separator
15464  * @extends Roo.menu.BaseItem
15465  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15466  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15467  * @constructor
15468  * @param {Object} config Configuration options
15469  */
15470 Roo.menu.Separator = function(config){
15471     Roo.menu.Separator.superclass.constructor.call(this, config);
15472 };
15473
15474 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15475     /**
15476      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15477      */
15478     itemCls : "x-menu-sep",
15479     /**
15480      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15481      */
15482     hideOnClick : false,
15483
15484     // private
15485     onRender : function(li){
15486         var s = document.createElement("span");
15487         s.className = this.itemCls;
15488         s.innerHTML = "&#160;";
15489         this.el = s;
15490         li.addClass("x-menu-sep-li");
15491         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15492     }
15493 });/*
15494  * Based on:
15495  * Ext JS Library 1.1.1
15496  * Copyright(c) 2006-2007, Ext JS, LLC.
15497  *
15498  * Originally Released Under LGPL - original licence link has changed is not relivant.
15499  *
15500  * Fork - LGPL
15501  * <script type="text/javascript">
15502  */
15503 /**
15504  * @class Roo.menu.Item
15505  * @extends Roo.menu.BaseItem
15506  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15507  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15508  * activation and click handling.
15509  * @constructor
15510  * Creates a new Item
15511  * @param {Object} config Configuration options
15512  */
15513 Roo.menu.Item = function(config){
15514     Roo.menu.Item.superclass.constructor.call(this, config);
15515     if(this.menu){
15516         this.menu = Roo.menu.MenuMgr.get(this.menu);
15517     }
15518 };
15519 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15520     /**
15521      * @cfg {Roo.menu.Menu} menu
15522      * A Sub menu
15523      */
15524     /**
15525      * @cfg {String} text
15526      * The text to show on the menu item.
15527      */
15528     text: '',
15529      /**
15530      * @cfg {String} html to render in menu
15531      * The text to show on the menu item (HTML version).
15532      */
15533     html: '',
15534     /**
15535      * @cfg {String} icon
15536      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15537      */
15538     icon: undefined,
15539     /**
15540      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15541      */
15542     itemCls : "x-menu-item",
15543     /**
15544      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15545      */
15546     canActivate : true,
15547     /**
15548      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15549      */
15550     showDelay: 200,
15551     // doc'd in BaseItem
15552     hideDelay: 200,
15553
15554     // private
15555     ctype: "Roo.menu.Item",
15556     
15557     // private
15558     onRender : function(container, position){
15559         var el = document.createElement("a");
15560         el.hideFocus = true;
15561         el.unselectable = "on";
15562         el.href = this.href || "#";
15563         if(this.hrefTarget){
15564             el.target = this.hrefTarget;
15565         }
15566         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15567         
15568         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15569         
15570         el.innerHTML = String.format(
15571                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15572                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15573         this.el = el;
15574         Roo.menu.Item.superclass.onRender.call(this, container, position);
15575     },
15576
15577     /**
15578      * Sets the text to display in this menu item
15579      * @param {String} text The text to display
15580      * @param {Boolean} isHTML true to indicate text is pure html.
15581      */
15582     setText : function(text, isHTML){
15583         if (isHTML) {
15584             this.html = text;
15585         } else {
15586             this.text = text;
15587             this.html = '';
15588         }
15589         if(this.rendered){
15590             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15591      
15592             this.el.update(String.format(
15593                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15594                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15595             this.parentMenu.autoWidth();
15596         }
15597     },
15598
15599     // private
15600     handleClick : function(e){
15601         if(!this.href){ // if no link defined, stop the event automatically
15602             e.stopEvent();
15603         }
15604         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15605     },
15606
15607     // private
15608     activate : function(autoExpand){
15609         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15610             this.focus();
15611             if(autoExpand){
15612                 this.expandMenu();
15613             }
15614         }
15615         return true;
15616     },
15617
15618     // private
15619     shouldDeactivate : function(e){
15620         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15621             if(this.menu && this.menu.isVisible()){
15622                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15623             }
15624             return true;
15625         }
15626         return false;
15627     },
15628
15629     // private
15630     deactivate : function(){
15631         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15632         this.hideMenu();
15633     },
15634
15635     // private
15636     expandMenu : function(autoActivate){
15637         if(!this.disabled && this.menu){
15638             clearTimeout(this.hideTimer);
15639             delete this.hideTimer;
15640             if(!this.menu.isVisible() && !this.showTimer){
15641                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15642             }else if (this.menu.isVisible() && autoActivate){
15643                 this.menu.tryActivate(0, 1);
15644             }
15645         }
15646     },
15647
15648     // private
15649     deferExpand : function(autoActivate){
15650         delete this.showTimer;
15651         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15652         if(autoActivate){
15653             this.menu.tryActivate(0, 1);
15654         }
15655     },
15656
15657     // private
15658     hideMenu : function(){
15659         clearTimeout(this.showTimer);
15660         delete this.showTimer;
15661         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15662             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15663         }
15664     },
15665
15666     // private
15667     deferHide : function(){
15668         delete this.hideTimer;
15669         this.menu.hide();
15670     }
15671 });/*
15672  * Based on:
15673  * Ext JS Library 1.1.1
15674  * Copyright(c) 2006-2007, Ext JS, LLC.
15675  *
15676  * Originally Released Under LGPL - original licence link has changed is not relivant.
15677  *
15678  * Fork - LGPL
15679  * <script type="text/javascript">
15680  */
15681  
15682 /**
15683  * @class Roo.menu.CheckItem
15684  * @extends Roo.menu.Item
15685  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15686  * @constructor
15687  * Creates a new CheckItem
15688  * @param {Object} config Configuration options
15689  */
15690 Roo.menu.CheckItem = function(config){
15691     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15692     this.addEvents({
15693         /**
15694          * @event beforecheckchange
15695          * Fires before the checked value is set, providing an opportunity to cancel if needed
15696          * @param {Roo.menu.CheckItem} this
15697          * @param {Boolean} checked The new checked value that will be set
15698          */
15699         "beforecheckchange" : true,
15700         /**
15701          * @event checkchange
15702          * Fires after the checked value has been set
15703          * @param {Roo.menu.CheckItem} this
15704          * @param {Boolean} checked The checked value that was set
15705          */
15706         "checkchange" : true
15707     });
15708     if(this.checkHandler){
15709         this.on('checkchange', this.checkHandler, this.scope);
15710     }
15711 };
15712 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15713     /**
15714      * @cfg {String} group
15715      * All check items with the same group name will automatically be grouped into a single-select
15716      * radio button group (defaults to '')
15717      */
15718     /**
15719      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15720      */
15721     itemCls : "x-menu-item x-menu-check-item",
15722     /**
15723      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15724      */
15725     groupClass : "x-menu-group-item",
15726
15727     /**
15728      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15729      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15730      * initialized with checked = true will be rendered as checked.
15731      */
15732     checked: false,
15733
15734     // private
15735     ctype: "Roo.menu.CheckItem",
15736
15737     // private
15738     onRender : function(c){
15739         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15740         if(this.group){
15741             this.el.addClass(this.groupClass);
15742         }
15743         Roo.menu.MenuMgr.registerCheckable(this);
15744         if(this.checked){
15745             this.checked = false;
15746             this.setChecked(true, true);
15747         }
15748     },
15749
15750     // private
15751     destroy : function(){
15752         if(this.rendered){
15753             Roo.menu.MenuMgr.unregisterCheckable(this);
15754         }
15755         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15756     },
15757
15758     /**
15759      * Set the checked state of this item
15760      * @param {Boolean} checked The new checked value
15761      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15762      */
15763     setChecked : function(state, suppressEvent){
15764         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15765             if(this.container){
15766                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15767             }
15768             this.checked = state;
15769             if(suppressEvent !== true){
15770                 this.fireEvent("checkchange", this, state);
15771             }
15772         }
15773     },
15774
15775     // private
15776     handleClick : function(e){
15777        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15778            this.setChecked(!this.checked);
15779        }
15780        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15781     }
15782 });/*
15783  * Based on:
15784  * Ext JS Library 1.1.1
15785  * Copyright(c) 2006-2007, Ext JS, LLC.
15786  *
15787  * Originally Released Under LGPL - original licence link has changed is not relivant.
15788  *
15789  * Fork - LGPL
15790  * <script type="text/javascript">
15791  */
15792  
15793 /**
15794  * @class Roo.menu.DateItem
15795  * @extends Roo.menu.Adapter
15796  * A menu item that wraps the {@link Roo.DatPicker} component.
15797  * @constructor
15798  * Creates a new DateItem
15799  * @param {Object} config Configuration options
15800  */
15801 Roo.menu.DateItem = function(config){
15802     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15803     /** The Roo.DatePicker object @type Roo.DatePicker */
15804     this.picker = this.component;
15805     this.addEvents({select: true});
15806     
15807     this.picker.on("render", function(picker){
15808         picker.getEl().swallowEvent("click");
15809         picker.container.addClass("x-menu-date-item");
15810     });
15811
15812     this.picker.on("select", this.onSelect, this);
15813 };
15814
15815 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15816     // private
15817     onSelect : function(picker, date){
15818         this.fireEvent("select", this, date, picker);
15819         Roo.menu.DateItem.superclass.handleClick.call(this);
15820     }
15821 });/*
15822  * Based on:
15823  * Ext JS Library 1.1.1
15824  * Copyright(c) 2006-2007, Ext JS, LLC.
15825  *
15826  * Originally Released Under LGPL - original licence link has changed is not relivant.
15827  *
15828  * Fork - LGPL
15829  * <script type="text/javascript">
15830  */
15831  
15832 /**
15833  * @class Roo.menu.ColorItem
15834  * @extends Roo.menu.Adapter
15835  * A menu item that wraps the {@link Roo.ColorPalette} component.
15836  * @constructor
15837  * Creates a new ColorItem
15838  * @param {Object} config Configuration options
15839  */
15840 Roo.menu.ColorItem = function(config){
15841     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15842     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15843     this.palette = this.component;
15844     this.relayEvents(this.palette, ["select"]);
15845     if(this.selectHandler){
15846         this.on('select', this.selectHandler, this.scope);
15847     }
15848 };
15849 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15850  * Based on:
15851  * Ext JS Library 1.1.1
15852  * Copyright(c) 2006-2007, Ext JS, LLC.
15853  *
15854  * Originally Released Under LGPL - original licence link has changed is not relivant.
15855  *
15856  * Fork - LGPL
15857  * <script type="text/javascript">
15858  */
15859  
15860
15861 /**
15862  * @class Roo.menu.DateMenu
15863  * @extends Roo.menu.Menu
15864  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15865  * @constructor
15866  * Creates a new DateMenu
15867  * @param {Object} config Configuration options
15868  */
15869 Roo.menu.DateMenu = function(config){
15870     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15871     this.plain = true;
15872     var di = new Roo.menu.DateItem(config);
15873     this.add(di);
15874     /**
15875      * The {@link Roo.DatePicker} instance for this DateMenu
15876      * @type DatePicker
15877      */
15878     this.picker = di.picker;
15879     /**
15880      * @event select
15881      * @param {DatePicker} picker
15882      * @param {Date} date
15883      */
15884     this.relayEvents(di, ["select"]);
15885     this.on('beforeshow', function(){
15886         if(this.picker){
15887             this.picker.hideMonthPicker(false);
15888         }
15889     }, this);
15890 };
15891 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15892     cls:'x-date-menu'
15893 });/*
15894  * Based on:
15895  * Ext JS Library 1.1.1
15896  * Copyright(c) 2006-2007, Ext JS, LLC.
15897  *
15898  * Originally Released Under LGPL - original licence link has changed is not relivant.
15899  *
15900  * Fork - LGPL
15901  * <script type="text/javascript">
15902  */
15903  
15904
15905 /**
15906  * @class Roo.menu.ColorMenu
15907  * @extends Roo.menu.Menu
15908  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15909  * @constructor
15910  * Creates a new ColorMenu
15911  * @param {Object} config Configuration options
15912  */
15913 Roo.menu.ColorMenu = function(config){
15914     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15915     this.plain = true;
15916     var ci = new Roo.menu.ColorItem(config);
15917     this.add(ci);
15918     /**
15919      * The {@link Roo.ColorPalette} instance for this ColorMenu
15920      * @type ColorPalette
15921      */
15922     this.palette = ci.palette;
15923     /**
15924      * @event select
15925      * @param {ColorPalette} palette
15926      * @param {String} color
15927      */
15928     this.relayEvents(ci, ["select"]);
15929 };
15930 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15931  * Based on:
15932  * Ext JS Library 1.1.1
15933  * Copyright(c) 2006-2007, Ext JS, LLC.
15934  *
15935  * Originally Released Under LGPL - original licence link has changed is not relivant.
15936  *
15937  * Fork - LGPL
15938  * <script type="text/javascript">
15939  */
15940  
15941 /**
15942  * @class Roo.form.TextItem
15943  * @extends Roo.BoxComponent
15944  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15945  * @constructor
15946  * Creates a new TextItem
15947  * @param {Object} config Configuration options
15948  */
15949 Roo.form.TextItem = function(config){
15950     Roo.form.TextItem.superclass.constructor.call(this, config);
15951 };
15952
15953 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15954     
15955     /**
15956      * @cfg {String} tag the tag for this item (default div)
15957      */
15958     tag : 'div',
15959     /**
15960      * @cfg {String} html the content for this item
15961      */
15962     html : '',
15963     
15964     getAutoCreate : function()
15965     {
15966         var cfg = {
15967             id: this.id,
15968             tag: this.tag,
15969             html: this.html,
15970             cls: 'x-form-item'
15971         };
15972         
15973         return cfg;
15974         
15975     },
15976     
15977     onRender : function(ct, position)
15978     {
15979         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15980         
15981         if(!this.el){
15982             var cfg = this.getAutoCreate();
15983             if(!cfg.name){
15984                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15985             }
15986             if (!cfg.name.length) {
15987                 delete cfg.name;
15988             }
15989             this.el = ct.createChild(cfg, position);
15990         }
15991     },
15992     /*
15993      * setHTML
15994      * @param {String} html update the Contents of the element.
15995      */
15996     setHTML : function(html)
15997     {
15998         this.fieldEl.dom.innerHTML = html;
15999     }
16000     
16001 });/*
16002  * Based on:
16003  * Ext JS Library 1.1.1
16004  * Copyright(c) 2006-2007, Ext JS, LLC.
16005  *
16006  * Originally Released Under LGPL - original licence link has changed is not relivant.
16007  *
16008  * Fork - LGPL
16009  * <script type="text/javascript">
16010  */
16011  
16012 /**
16013  * @class Roo.form.Field
16014  * @extends Roo.BoxComponent
16015  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16016  * @constructor
16017  * Creates a new Field
16018  * @param {Object} config Configuration options
16019  */
16020 Roo.form.Field = function(config){
16021     Roo.form.Field.superclass.constructor.call(this, config);
16022 };
16023
16024 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16025     /**
16026      * @cfg {String} fieldLabel Label to use when rendering a form.
16027      */
16028        /**
16029      * @cfg {String} qtip Mouse over tip
16030      */
16031      
16032     /**
16033      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16034      */
16035     invalidClass : "x-form-invalid",
16036     /**
16037      * @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")
16038      */
16039     invalidText : "The value in this field is invalid",
16040     /**
16041      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16042      */
16043     focusClass : "x-form-focus",
16044     /**
16045      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16046       automatic validation (defaults to "keyup").
16047      */
16048     validationEvent : "keyup",
16049     /**
16050      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16051      */
16052     validateOnBlur : true,
16053     /**
16054      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16055      */
16056     validationDelay : 250,
16057     /**
16058      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16059      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16060      */
16061     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16062     /**
16063      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16064      */
16065     fieldClass : "x-form-field",
16066     /**
16067      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16068      *<pre>
16069 Value         Description
16070 -----------   ----------------------------------------------------------------------
16071 qtip          Display a quick tip when the user hovers over the field
16072 title         Display a default browser title attribute popup
16073 under         Add a block div beneath the field containing the error text
16074 side          Add an error icon to the right of the field with a popup on hover
16075 [element id]  Add the error text directly to the innerHTML of the specified element
16076 </pre>
16077      */
16078     msgTarget : 'qtip',
16079     /**
16080      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16081      */
16082     msgFx : 'normal',
16083
16084     /**
16085      * @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.
16086      */
16087     readOnly : false,
16088
16089     /**
16090      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16091      */
16092     disabled : false,
16093
16094     /**
16095      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16096      */
16097     inputType : undefined,
16098     
16099     /**
16100      * @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).
16101          */
16102         tabIndex : undefined,
16103         
16104     // private
16105     isFormField : true,
16106
16107     // private
16108     hasFocus : false,
16109     /**
16110      * @property {Roo.Element} fieldEl
16111      * Element Containing the rendered Field (with label etc.)
16112      */
16113     /**
16114      * @cfg {Mixed} value A value to initialize this field with.
16115      */
16116     value : undefined,
16117
16118     /**
16119      * @cfg {String} name The field's HTML name attribute.
16120      */
16121     /**
16122      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16123      */
16124     // private
16125     loadedValue : false,
16126      
16127      
16128         // private ??
16129         initComponent : function(){
16130         Roo.form.Field.superclass.initComponent.call(this);
16131         this.addEvents({
16132             /**
16133              * @event focus
16134              * Fires when this field receives input focus.
16135              * @param {Roo.form.Field} this
16136              */
16137             focus : true,
16138             /**
16139              * @event blur
16140              * Fires when this field loses input focus.
16141              * @param {Roo.form.Field} this
16142              */
16143             blur : true,
16144             /**
16145              * @event specialkey
16146              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16147              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16148              * @param {Roo.form.Field} this
16149              * @param {Roo.EventObject} e The event object
16150              */
16151             specialkey : true,
16152             /**
16153              * @event change
16154              * Fires just before the field blurs if the field value has changed.
16155              * @param {Roo.form.Field} this
16156              * @param {Mixed} newValue The new value
16157              * @param {Mixed} oldValue The original value
16158              */
16159             change : true,
16160             /**
16161              * @event invalid
16162              * Fires after the field has been marked as invalid.
16163              * @param {Roo.form.Field} this
16164              * @param {String} msg The validation message
16165              */
16166             invalid : true,
16167             /**
16168              * @event valid
16169              * Fires after the field has been validated with no errors.
16170              * @param {Roo.form.Field} this
16171              */
16172             valid : true,
16173              /**
16174              * @event keyup
16175              * Fires after the key up
16176              * @param {Roo.form.Field} this
16177              * @param {Roo.EventObject}  e The event Object
16178              */
16179             keyup : true
16180         });
16181     },
16182
16183     /**
16184      * Returns the name attribute of the field if available
16185      * @return {String} name The field name
16186      */
16187     getName: function(){
16188          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16189     },
16190
16191     // private
16192     onRender : function(ct, position){
16193         Roo.form.Field.superclass.onRender.call(this, ct, position);
16194         if(!this.el){
16195             var cfg = this.getAutoCreate();
16196             if(!cfg.name){
16197                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16198             }
16199             if (!cfg.name.length) {
16200                 delete cfg.name;
16201             }
16202             if(this.inputType){
16203                 cfg.type = this.inputType;
16204             }
16205             this.el = ct.createChild(cfg, position);
16206         }
16207         var type = this.el.dom.type;
16208         if(type){
16209             if(type == 'password'){
16210                 type = 'text';
16211             }
16212             this.el.addClass('x-form-'+type);
16213         }
16214         if(this.readOnly){
16215             this.el.dom.readOnly = true;
16216         }
16217         if(this.tabIndex !== undefined){
16218             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16219         }
16220
16221         this.el.addClass([this.fieldClass, this.cls]);
16222         this.initValue();
16223     },
16224
16225     /**
16226      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16227      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16228      * @return {Roo.form.Field} this
16229      */
16230     applyTo : function(target){
16231         this.allowDomMove = false;
16232         this.el = Roo.get(target);
16233         this.render(this.el.dom.parentNode);
16234         return this;
16235     },
16236
16237     // private
16238     initValue : function(){
16239         if(this.value !== undefined){
16240             this.setValue(this.value);
16241         }else if(this.el.dom.value.length > 0){
16242             this.setValue(this.el.dom.value);
16243         }
16244     },
16245
16246     /**
16247      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16248      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16249      */
16250     isDirty : function() {
16251         if(this.disabled) {
16252             return false;
16253         }
16254         return String(this.getValue()) !== String(this.originalValue);
16255     },
16256
16257     /**
16258      * stores the current value in loadedValue
16259      */
16260     resetHasChanged : function()
16261     {
16262         this.loadedValue = String(this.getValue());
16263     },
16264     /**
16265      * checks the current value against the 'loaded' value.
16266      * Note - will return false if 'resetHasChanged' has not been called first.
16267      */
16268     hasChanged : function()
16269     {
16270         if(this.disabled || this.readOnly) {
16271             return false;
16272         }
16273         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16274     },
16275     
16276     
16277     
16278     // private
16279     afterRender : function(){
16280         Roo.form.Field.superclass.afterRender.call(this);
16281         this.initEvents();
16282     },
16283
16284     // private
16285     fireKey : function(e){
16286         //Roo.log('field ' + e.getKey());
16287         if(e.isNavKeyPress()){
16288             this.fireEvent("specialkey", this, e);
16289         }
16290     },
16291
16292     /**
16293      * Resets the current field value to the originally loaded value and clears any validation messages
16294      */
16295     reset : function(){
16296         this.setValue(this.resetValue);
16297         this.originalValue = this.getValue();
16298         this.clearInvalid();
16299     },
16300
16301     // private
16302     initEvents : function(){
16303         // safari killled keypress - so keydown is now used..
16304         this.el.on("keydown" , this.fireKey,  this);
16305         this.el.on("focus", this.onFocus,  this);
16306         this.el.on("blur", this.onBlur,  this);
16307         this.el.relayEvent('keyup', this);
16308
16309         // reference to original value for reset
16310         this.originalValue = this.getValue();
16311         this.resetValue =  this.getValue();
16312     },
16313
16314     // private
16315     onFocus : function(){
16316         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16317             this.el.addClass(this.focusClass);
16318         }
16319         if(!this.hasFocus){
16320             this.hasFocus = true;
16321             this.startValue = this.getValue();
16322             this.fireEvent("focus", this);
16323         }
16324     },
16325
16326     beforeBlur : Roo.emptyFn,
16327
16328     // private
16329     onBlur : function(){
16330         this.beforeBlur();
16331         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16332             this.el.removeClass(this.focusClass);
16333         }
16334         this.hasFocus = false;
16335         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16336             this.validate();
16337         }
16338         var v = this.getValue();
16339         if(String(v) !== String(this.startValue)){
16340             this.fireEvent('change', this, v, this.startValue);
16341         }
16342         this.fireEvent("blur", this);
16343     },
16344
16345     /**
16346      * Returns whether or not the field value is currently valid
16347      * @param {Boolean} preventMark True to disable marking the field invalid
16348      * @return {Boolean} True if the value is valid, else false
16349      */
16350     isValid : function(preventMark){
16351         if(this.disabled){
16352             return true;
16353         }
16354         var restore = this.preventMark;
16355         this.preventMark = preventMark === true;
16356         var v = this.validateValue(this.processValue(this.getRawValue()));
16357         this.preventMark = restore;
16358         return v;
16359     },
16360
16361     /**
16362      * Validates the field value
16363      * @return {Boolean} True if the value is valid, else false
16364      */
16365     validate : function(){
16366         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16367             this.clearInvalid();
16368             return true;
16369         }
16370         return false;
16371     },
16372
16373     processValue : function(value){
16374         return value;
16375     },
16376
16377     // private
16378     // Subclasses should provide the validation implementation by overriding this
16379     validateValue : function(value){
16380         return true;
16381     },
16382
16383     /**
16384      * Mark this field as invalid
16385      * @param {String} msg The validation message
16386      */
16387     markInvalid : function(msg){
16388         if(!this.rendered || this.preventMark){ // not rendered
16389             return;
16390         }
16391         
16392         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16393         
16394         obj.el.addClass(this.invalidClass);
16395         msg = msg || this.invalidText;
16396         switch(this.msgTarget){
16397             case 'qtip':
16398                 obj.el.dom.qtip = msg;
16399                 obj.el.dom.qclass = 'x-form-invalid-tip';
16400                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16401                     Roo.QuickTips.enable();
16402                 }
16403                 break;
16404             case 'title':
16405                 this.el.dom.title = msg;
16406                 break;
16407             case 'under':
16408                 if(!this.errorEl){
16409                     var elp = this.el.findParent('.x-form-element', 5, true);
16410                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16411                     this.errorEl.setWidth(elp.getWidth(true)-20);
16412                 }
16413                 this.errorEl.update(msg);
16414                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16415                 break;
16416             case 'side':
16417                 if(!this.errorIcon){
16418                     var elp = this.el.findParent('.x-form-element', 5, true);
16419                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16420                 }
16421                 this.alignErrorIcon();
16422                 this.errorIcon.dom.qtip = msg;
16423                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16424                 this.errorIcon.show();
16425                 this.on('resize', this.alignErrorIcon, this);
16426                 break;
16427             default:
16428                 var t = Roo.getDom(this.msgTarget);
16429                 t.innerHTML = msg;
16430                 t.style.display = this.msgDisplay;
16431                 break;
16432         }
16433         this.fireEvent('invalid', this, msg);
16434     },
16435
16436     // private
16437     alignErrorIcon : function(){
16438         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16439     },
16440
16441     /**
16442      * Clear any invalid styles/messages for this field
16443      */
16444     clearInvalid : function(){
16445         if(!this.rendered || this.preventMark){ // not rendered
16446             return;
16447         }
16448         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16449         
16450         obj.el.removeClass(this.invalidClass);
16451         switch(this.msgTarget){
16452             case 'qtip':
16453                 obj.el.dom.qtip = '';
16454                 break;
16455             case 'title':
16456                 this.el.dom.title = '';
16457                 break;
16458             case 'under':
16459                 if(this.errorEl){
16460                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16461                 }
16462                 break;
16463             case 'side':
16464                 if(this.errorIcon){
16465                     this.errorIcon.dom.qtip = '';
16466                     this.errorIcon.hide();
16467                     this.un('resize', this.alignErrorIcon, this);
16468                 }
16469                 break;
16470             default:
16471                 var t = Roo.getDom(this.msgTarget);
16472                 t.innerHTML = '';
16473                 t.style.display = 'none';
16474                 break;
16475         }
16476         this.fireEvent('valid', this);
16477     },
16478
16479     /**
16480      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16481      * @return {Mixed} value The field value
16482      */
16483     getRawValue : function(){
16484         var v = this.el.getValue();
16485         
16486         return v;
16487     },
16488
16489     /**
16490      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16491      * @return {Mixed} value The field value
16492      */
16493     getValue : function(){
16494         var v = this.el.getValue();
16495          
16496         return v;
16497     },
16498
16499     /**
16500      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16501      * @param {Mixed} value The value to set
16502      */
16503     setRawValue : function(v){
16504         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16505     },
16506
16507     /**
16508      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16509      * @param {Mixed} value The value to set
16510      */
16511     setValue : function(v){
16512         this.value = v;
16513         if(this.rendered){
16514             this.el.dom.value = (v === null || v === undefined ? '' : v);
16515              this.validate();
16516         }
16517     },
16518
16519     adjustSize : function(w, h){
16520         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16521         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16522         return s;
16523     },
16524
16525     adjustWidth : function(tag, w){
16526         tag = tag.toLowerCase();
16527         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16528             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16529                 if(tag == 'input'){
16530                     return w + 2;
16531                 }
16532                 if(tag == 'textarea'){
16533                     return w-2;
16534                 }
16535             }else if(Roo.isOpera){
16536                 if(tag == 'input'){
16537                     return w + 2;
16538                 }
16539                 if(tag == 'textarea'){
16540                     return w-2;
16541                 }
16542             }
16543         }
16544         return w;
16545     }
16546 });
16547
16548
16549 // anything other than normal should be considered experimental
16550 Roo.form.Field.msgFx = {
16551     normal : {
16552         show: function(msgEl, f){
16553             msgEl.setDisplayed('block');
16554         },
16555
16556         hide : function(msgEl, f){
16557             msgEl.setDisplayed(false).update('');
16558         }
16559     },
16560
16561     slide : {
16562         show: function(msgEl, f){
16563             msgEl.slideIn('t', {stopFx:true});
16564         },
16565
16566         hide : function(msgEl, f){
16567             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16568         }
16569     },
16570
16571     slideRight : {
16572         show: function(msgEl, f){
16573             msgEl.fixDisplay();
16574             msgEl.alignTo(f.el, 'tl-tr');
16575             msgEl.slideIn('l', {stopFx:true});
16576         },
16577
16578         hide : function(msgEl, f){
16579             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16580         }
16581     }
16582 };/*
16583  * Based on:
16584  * Ext JS Library 1.1.1
16585  * Copyright(c) 2006-2007, Ext JS, LLC.
16586  *
16587  * Originally Released Under LGPL - original licence link has changed is not relivant.
16588  *
16589  * Fork - LGPL
16590  * <script type="text/javascript">
16591  */
16592  
16593
16594 /**
16595  * @class Roo.form.TextField
16596  * @extends Roo.form.Field
16597  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16598  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16599  * @constructor
16600  * Creates a new TextField
16601  * @param {Object} config Configuration options
16602  */
16603 Roo.form.TextField = function(config){
16604     Roo.form.TextField.superclass.constructor.call(this, config);
16605     this.addEvents({
16606         /**
16607          * @event autosize
16608          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16609          * according to the default logic, but this event provides a hook for the developer to apply additional
16610          * logic at runtime to resize the field if needed.
16611              * @param {Roo.form.Field} this This text field
16612              * @param {Number} width The new field width
16613              */
16614         autosize : true
16615     });
16616 };
16617
16618 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16619     /**
16620      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16621      */
16622     grow : false,
16623     /**
16624      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16625      */
16626     growMin : 30,
16627     /**
16628      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16629      */
16630     growMax : 800,
16631     /**
16632      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16633      */
16634     vtype : null,
16635     /**
16636      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16637      */
16638     maskRe : null,
16639     /**
16640      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16641      */
16642     disableKeyFilter : false,
16643     /**
16644      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16645      */
16646     allowBlank : true,
16647     /**
16648      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16649      */
16650     minLength : 0,
16651     /**
16652      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16653      */
16654     maxLength : Number.MAX_VALUE,
16655     /**
16656      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16657      */
16658     minLengthText : "The minimum length for this field is {0}",
16659     /**
16660      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16661      */
16662     maxLengthText : "The maximum length for this field is {0}",
16663     /**
16664      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16665      */
16666     selectOnFocus : false,
16667     /**
16668      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16669      */    
16670     allowLeadingSpace : false,
16671     /**
16672      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16673      */
16674     blankText : "This field is required",
16675     /**
16676      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16677      * If available, this function will be called only after the basic validators all return true, and will be passed the
16678      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16679      */
16680     validator : null,
16681     /**
16682      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16683      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16684      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16685      */
16686     regex : null,
16687     /**
16688      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16689      */
16690     regexText : "",
16691     /**
16692      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16693      */
16694     emptyText : null,
16695    
16696
16697     // private
16698     initEvents : function()
16699     {
16700         if (this.emptyText) {
16701             this.el.attr('placeholder', this.emptyText);
16702         }
16703         
16704         Roo.form.TextField.superclass.initEvents.call(this);
16705         if(this.validationEvent == 'keyup'){
16706             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16707             this.el.on('keyup', this.filterValidation, this);
16708         }
16709         else if(this.validationEvent !== false){
16710             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16711         }
16712         
16713         if(this.selectOnFocus){
16714             this.on("focus", this.preFocus, this);
16715         }
16716         if (!this.allowLeadingSpace) {
16717             this.on('blur', this.cleanLeadingSpace, this);
16718         }
16719         
16720         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16721             this.el.on("keypress", this.filterKeys, this);
16722         }
16723         if(this.grow){
16724             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16725             this.el.on("click", this.autoSize,  this);
16726         }
16727         if(this.el.is('input[type=password]') && Roo.isSafari){
16728             this.el.on('keydown', this.SafariOnKeyDown, this);
16729         }
16730     },
16731
16732     processValue : function(value){
16733         if(this.stripCharsRe){
16734             var newValue = value.replace(this.stripCharsRe, '');
16735             if(newValue !== value){
16736                 this.setRawValue(newValue);
16737                 return newValue;
16738             }
16739         }
16740         return value;
16741     },
16742
16743     filterValidation : function(e){
16744         if(!e.isNavKeyPress()){
16745             this.validationTask.delay(this.validationDelay);
16746         }
16747     },
16748
16749     // private
16750     onKeyUp : function(e){
16751         if(!e.isNavKeyPress()){
16752             this.autoSize();
16753         }
16754     },
16755     // private - clean the leading white space
16756     cleanLeadingSpace : function(e)
16757     {
16758         if ( this.inputType == 'file') {
16759             return;
16760         }
16761         
16762         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16763     },
16764     /**
16765      * Resets the current field value to the originally-loaded value and clears any validation messages.
16766      *  
16767      */
16768     reset : function(){
16769         Roo.form.TextField.superclass.reset.call(this);
16770        
16771     }, 
16772     // private
16773     preFocus : function(){
16774         
16775         if(this.selectOnFocus){
16776             this.el.dom.select();
16777         }
16778     },
16779
16780     
16781     // private
16782     filterKeys : function(e){
16783         var k = e.getKey();
16784         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16785             return;
16786         }
16787         var c = e.getCharCode(), cc = String.fromCharCode(c);
16788         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16789             return;
16790         }
16791         if(!this.maskRe.test(cc)){
16792             e.stopEvent();
16793         }
16794     },
16795
16796     setValue : function(v){
16797         
16798         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16799         
16800         this.autoSize();
16801     },
16802
16803     /**
16804      * Validates a value according to the field's validation rules and marks the field as invalid
16805      * if the validation fails
16806      * @param {Mixed} value The value to validate
16807      * @return {Boolean} True if the value is valid, else false
16808      */
16809     validateValue : function(value){
16810         if(value.length < 1)  { // if it's blank
16811              if(this.allowBlank){
16812                 this.clearInvalid();
16813                 return true;
16814              }else{
16815                 this.markInvalid(this.blankText);
16816                 return false;
16817              }
16818         }
16819         if(value.length < this.minLength){
16820             this.markInvalid(String.format(this.minLengthText, this.minLength));
16821             return false;
16822         }
16823         if(value.length > this.maxLength){
16824             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16825             return false;
16826         }
16827         if(this.vtype){
16828             var vt = Roo.form.VTypes;
16829             if(!vt[this.vtype](value, this)){
16830                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16831                 return false;
16832             }
16833         }
16834         if(typeof this.validator == "function"){
16835             var msg = this.validator(value);
16836             if(msg !== true){
16837                 this.markInvalid(msg);
16838                 return false;
16839             }
16840         }
16841         if(this.regex && !this.regex.test(value)){
16842             this.markInvalid(this.regexText);
16843             return false;
16844         }
16845         return true;
16846     },
16847
16848     /**
16849      * Selects text in this field
16850      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16851      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16852      */
16853     selectText : function(start, end){
16854         var v = this.getRawValue();
16855         if(v.length > 0){
16856             start = start === undefined ? 0 : start;
16857             end = end === undefined ? v.length : end;
16858             var d = this.el.dom;
16859             if(d.setSelectionRange){
16860                 d.setSelectionRange(start, end);
16861             }else if(d.createTextRange){
16862                 var range = d.createTextRange();
16863                 range.moveStart("character", start);
16864                 range.moveEnd("character", v.length-end);
16865                 range.select();
16866             }
16867         }
16868     },
16869
16870     /**
16871      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16872      * This only takes effect if grow = true, and fires the autosize event.
16873      */
16874     autoSize : function(){
16875         if(!this.grow || !this.rendered){
16876             return;
16877         }
16878         if(!this.metrics){
16879             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16880         }
16881         var el = this.el;
16882         var v = el.dom.value;
16883         var d = document.createElement('div');
16884         d.appendChild(document.createTextNode(v));
16885         v = d.innerHTML;
16886         d = null;
16887         v += "&#160;";
16888         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16889         this.el.setWidth(w);
16890         this.fireEvent("autosize", this, w);
16891     },
16892     
16893     // private
16894     SafariOnKeyDown : function(event)
16895     {
16896         // this is a workaround for a password hang bug on chrome/ webkit.
16897         
16898         var isSelectAll = false;
16899         
16900         if(this.el.dom.selectionEnd > 0){
16901             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16902         }
16903         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16904             event.preventDefault();
16905             this.setValue('');
16906             return;
16907         }
16908         
16909         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16910             
16911             event.preventDefault();
16912             // this is very hacky as keydown always get's upper case.
16913             
16914             var cc = String.fromCharCode(event.getCharCode());
16915             
16916             
16917             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16918             
16919         }
16920         
16921         
16922     }
16923 });/*
16924  * Based on:
16925  * Ext JS Library 1.1.1
16926  * Copyright(c) 2006-2007, Ext JS, LLC.
16927  *
16928  * Originally Released Under LGPL - original licence link has changed is not relivant.
16929  *
16930  * Fork - LGPL
16931  * <script type="text/javascript">
16932  */
16933  
16934 /**
16935  * @class Roo.form.Hidden
16936  * @extends Roo.form.TextField
16937  * Simple Hidden element used on forms 
16938  * 
16939  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16940  * 
16941  * @constructor
16942  * Creates a new Hidden form element.
16943  * @param {Object} config Configuration options
16944  */
16945
16946
16947
16948 // easy hidden field...
16949 Roo.form.Hidden = function(config){
16950     Roo.form.Hidden.superclass.constructor.call(this, config);
16951 };
16952   
16953 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16954     fieldLabel:      '',
16955     inputType:      'hidden',
16956     width:          50,
16957     allowBlank:     true,
16958     labelSeparator: '',
16959     hidden:         true,
16960     itemCls :       'x-form-item-display-none'
16961
16962
16963 });
16964
16965
16966 /*
16967  * Based on:
16968  * Ext JS Library 1.1.1
16969  * Copyright(c) 2006-2007, Ext JS, LLC.
16970  *
16971  * Originally Released Under LGPL - original licence link has changed is not relivant.
16972  *
16973  * Fork - LGPL
16974  * <script type="text/javascript">
16975  */
16976  
16977 /**
16978  * @class Roo.form.TriggerField
16979  * @extends Roo.form.TextField
16980  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16981  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16982  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16983  * for which you can provide a custom implementation.  For example:
16984  * <pre><code>
16985 var trigger = new Roo.form.TriggerField();
16986 trigger.onTriggerClick = myTriggerFn;
16987 trigger.applyTo('my-field');
16988 </code></pre>
16989  *
16990  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16991  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16992  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16993  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16994  * @constructor
16995  * Create a new TriggerField.
16996  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16997  * to the base TextField)
16998  */
16999 Roo.form.TriggerField = function(config){
17000     this.mimicing = false;
17001     Roo.form.TriggerField.superclass.constructor.call(this, config);
17002 };
17003
17004 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17005     /**
17006      * @cfg {String} triggerClass A CSS class to apply to the trigger
17007      */
17008     /**
17009      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17010      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17011      */
17012     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17013     /**
17014      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17015      */
17016     hideTrigger:false,
17017
17018     /** @cfg {Boolean} grow @hide */
17019     /** @cfg {Number} growMin @hide */
17020     /** @cfg {Number} growMax @hide */
17021
17022     /**
17023      * @hide 
17024      * @method
17025      */
17026     autoSize: Roo.emptyFn,
17027     // private
17028     monitorTab : true,
17029     // private
17030     deferHeight : true,
17031
17032     
17033     actionMode : 'wrap',
17034     // private
17035     onResize : function(w, h){
17036         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17037         if(typeof w == 'number'){
17038             var x = w - this.trigger.getWidth();
17039             this.el.setWidth(this.adjustWidth('input', x));
17040             this.trigger.setStyle('left', x+'px');
17041         }
17042     },
17043
17044     // private
17045     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17046
17047     // private
17048     getResizeEl : function(){
17049         return this.wrap;
17050     },
17051
17052     // private
17053     getPositionEl : function(){
17054         return this.wrap;
17055     },
17056
17057     // private
17058     alignErrorIcon : function(){
17059         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17060     },
17061
17062     // private
17063     onRender : function(ct, position){
17064         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17065         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17066         this.trigger = this.wrap.createChild(this.triggerConfig ||
17067                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17068         if(this.hideTrigger){
17069             this.trigger.setDisplayed(false);
17070         }
17071         this.initTrigger();
17072         if(!this.width){
17073             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17074         }
17075     },
17076
17077     // private
17078     initTrigger : function(){
17079         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17080         this.trigger.addClassOnOver('x-form-trigger-over');
17081         this.trigger.addClassOnClick('x-form-trigger-click');
17082     },
17083
17084     // private
17085     onDestroy : function(){
17086         if(this.trigger){
17087             this.trigger.removeAllListeners();
17088             this.trigger.remove();
17089         }
17090         if(this.wrap){
17091             this.wrap.remove();
17092         }
17093         Roo.form.TriggerField.superclass.onDestroy.call(this);
17094     },
17095
17096     // private
17097     onFocus : function(){
17098         Roo.form.TriggerField.superclass.onFocus.call(this);
17099         if(!this.mimicing){
17100             this.wrap.addClass('x-trigger-wrap-focus');
17101             this.mimicing = true;
17102             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17103             if(this.monitorTab){
17104                 this.el.on("keydown", this.checkTab, this);
17105             }
17106         }
17107     },
17108
17109     // private
17110     checkTab : function(e){
17111         if(e.getKey() == e.TAB){
17112             this.triggerBlur();
17113         }
17114     },
17115
17116     // private
17117     onBlur : function(){
17118         // do nothing
17119     },
17120
17121     // private
17122     mimicBlur : function(e, t){
17123         if(!this.wrap.contains(t) && this.validateBlur()){
17124             this.triggerBlur();
17125         }
17126     },
17127
17128     // private
17129     triggerBlur : function(){
17130         this.mimicing = false;
17131         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17132         if(this.monitorTab){
17133             this.el.un("keydown", this.checkTab, this);
17134         }
17135         this.wrap.removeClass('x-trigger-wrap-focus');
17136         Roo.form.TriggerField.superclass.onBlur.call(this);
17137     },
17138
17139     // private
17140     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17141     validateBlur : function(e, t){
17142         return true;
17143     },
17144
17145     // private
17146     onDisable : function(){
17147         Roo.form.TriggerField.superclass.onDisable.call(this);
17148         if(this.wrap){
17149             this.wrap.addClass('x-item-disabled');
17150         }
17151     },
17152
17153     // private
17154     onEnable : function(){
17155         Roo.form.TriggerField.superclass.onEnable.call(this);
17156         if(this.wrap){
17157             this.wrap.removeClass('x-item-disabled');
17158         }
17159     },
17160
17161     // private
17162     onShow : function(){
17163         var ae = this.getActionEl();
17164         
17165         if(ae){
17166             ae.dom.style.display = '';
17167             ae.dom.style.visibility = 'visible';
17168         }
17169     },
17170
17171     // private
17172     
17173     onHide : function(){
17174         var ae = this.getActionEl();
17175         ae.dom.style.display = 'none';
17176     },
17177
17178     /**
17179      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17180      * by an implementing function.
17181      * @method
17182      * @param {EventObject} e
17183      */
17184     onTriggerClick : Roo.emptyFn
17185 });
17186
17187 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17188 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17189 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17190 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17191     initComponent : function(){
17192         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17193
17194         this.triggerConfig = {
17195             tag:'span', cls:'x-form-twin-triggers', cn:[
17196             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17197             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17198         ]};
17199     },
17200
17201     getTrigger : function(index){
17202         return this.triggers[index];
17203     },
17204
17205     initTrigger : function(){
17206         var ts = this.trigger.select('.x-form-trigger', true);
17207         this.wrap.setStyle('overflow', 'hidden');
17208         var triggerField = this;
17209         ts.each(function(t, all, index){
17210             t.hide = function(){
17211                 var w = triggerField.wrap.getWidth();
17212                 this.dom.style.display = 'none';
17213                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17214             };
17215             t.show = function(){
17216                 var w = triggerField.wrap.getWidth();
17217                 this.dom.style.display = '';
17218                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17219             };
17220             var triggerIndex = 'Trigger'+(index+1);
17221
17222             if(this['hide'+triggerIndex]){
17223                 t.dom.style.display = 'none';
17224             }
17225             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17226             t.addClassOnOver('x-form-trigger-over');
17227             t.addClassOnClick('x-form-trigger-click');
17228         }, this);
17229         this.triggers = ts.elements;
17230     },
17231
17232     onTrigger1Click : Roo.emptyFn,
17233     onTrigger2Click : Roo.emptyFn
17234 });/*
17235  * Based on:
17236  * Ext JS Library 1.1.1
17237  * Copyright(c) 2006-2007, Ext JS, LLC.
17238  *
17239  * Originally Released Under LGPL - original licence link has changed is not relivant.
17240  *
17241  * Fork - LGPL
17242  * <script type="text/javascript">
17243  */
17244  
17245 /**
17246  * @class Roo.form.TextArea
17247  * @extends Roo.form.TextField
17248  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17249  * support for auto-sizing.
17250  * @constructor
17251  * Creates a new TextArea
17252  * @param {Object} config Configuration options
17253  */
17254 Roo.form.TextArea = function(config){
17255     Roo.form.TextArea.superclass.constructor.call(this, config);
17256     // these are provided exchanges for backwards compat
17257     // minHeight/maxHeight were replaced by growMin/growMax to be
17258     // compatible with TextField growing config values
17259     if(this.minHeight !== undefined){
17260         this.growMin = this.minHeight;
17261     }
17262     if(this.maxHeight !== undefined){
17263         this.growMax = this.maxHeight;
17264     }
17265 };
17266
17267 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17268     /**
17269      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17270      */
17271     growMin : 60,
17272     /**
17273      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17274      */
17275     growMax: 1000,
17276     /**
17277      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17278      * in the field (equivalent to setting overflow: hidden, defaults to false)
17279      */
17280     preventScrollbars: false,
17281     /**
17282      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17283      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17284      */
17285
17286     // private
17287     onRender : function(ct, position){
17288         if(!this.el){
17289             this.defaultAutoCreate = {
17290                 tag: "textarea",
17291                 style:"width:300px;height:60px;",
17292                 autocomplete: "new-password"
17293             };
17294         }
17295         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17296         if(this.grow){
17297             this.textSizeEl = Roo.DomHelper.append(document.body, {
17298                 tag: "pre", cls: "x-form-grow-sizer"
17299             });
17300             if(this.preventScrollbars){
17301                 this.el.setStyle("overflow", "hidden");
17302             }
17303             this.el.setHeight(this.growMin);
17304         }
17305     },
17306
17307     onDestroy : function(){
17308         if(this.textSizeEl){
17309             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17310         }
17311         Roo.form.TextArea.superclass.onDestroy.call(this);
17312     },
17313
17314     // private
17315     onKeyUp : function(e){
17316         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17317             this.autoSize();
17318         }
17319     },
17320
17321     /**
17322      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17323      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17324      */
17325     autoSize : function(){
17326         if(!this.grow || !this.textSizeEl){
17327             return;
17328         }
17329         var el = this.el;
17330         var v = el.dom.value;
17331         var ts = this.textSizeEl;
17332
17333         ts.innerHTML = '';
17334         ts.appendChild(document.createTextNode(v));
17335         v = ts.innerHTML;
17336
17337         Roo.fly(ts).setWidth(this.el.getWidth());
17338         if(v.length < 1){
17339             v = "&#160;&#160;";
17340         }else{
17341             if(Roo.isIE){
17342                 v = v.replace(/\n/g, '<p>&#160;</p>');
17343             }
17344             v += "&#160;\n&#160;";
17345         }
17346         ts.innerHTML = v;
17347         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17348         if(h != this.lastHeight){
17349             this.lastHeight = h;
17350             this.el.setHeight(h);
17351             this.fireEvent("autosize", this, h);
17352         }
17353     }
17354 });/*
17355  * Based on:
17356  * Ext JS Library 1.1.1
17357  * Copyright(c) 2006-2007, Ext JS, LLC.
17358  *
17359  * Originally Released Under LGPL - original licence link has changed is not relivant.
17360  *
17361  * Fork - LGPL
17362  * <script type="text/javascript">
17363  */
17364  
17365
17366 /**
17367  * @class Roo.form.NumberField
17368  * @extends Roo.form.TextField
17369  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17370  * @constructor
17371  * Creates a new NumberField
17372  * @param {Object} config Configuration options
17373  */
17374 Roo.form.NumberField = function(config){
17375     Roo.form.NumberField.superclass.constructor.call(this, config);
17376 };
17377
17378 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17379     /**
17380      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17381      */
17382     fieldClass: "x-form-field x-form-num-field",
17383     /**
17384      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17385      */
17386     allowDecimals : true,
17387     /**
17388      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17389      */
17390     decimalSeparator : ".",
17391     /**
17392      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17393      */
17394     decimalPrecision : 2,
17395     /**
17396      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17397      */
17398     allowNegative : true,
17399     /**
17400      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17401      */
17402     minValue : Number.NEGATIVE_INFINITY,
17403     /**
17404      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17405      */
17406     maxValue : Number.MAX_VALUE,
17407     /**
17408      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17409      */
17410     minText : "The minimum value for this field is {0}",
17411     /**
17412      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17413      */
17414     maxText : "The maximum value for this field is {0}",
17415     /**
17416      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17417      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17418      */
17419     nanText : "{0} is not a valid number",
17420
17421     // private
17422     initEvents : function(){
17423         Roo.form.NumberField.superclass.initEvents.call(this);
17424         var allowed = "0123456789";
17425         if(this.allowDecimals){
17426             allowed += this.decimalSeparator;
17427         }
17428         if(this.allowNegative){
17429             allowed += "-";
17430         }
17431         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17432         var keyPress = function(e){
17433             var k = e.getKey();
17434             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17435                 return;
17436             }
17437             var c = e.getCharCode();
17438             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17439                 e.stopEvent();
17440             }
17441         };
17442         this.el.on("keypress", keyPress, this);
17443     },
17444
17445     // private
17446     validateValue : function(value){
17447         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17448             return false;
17449         }
17450         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17451              return true;
17452         }
17453         var num = this.parseValue(value);
17454         if(isNaN(num)){
17455             this.markInvalid(String.format(this.nanText, value));
17456             return false;
17457         }
17458         if(num < this.minValue){
17459             this.markInvalid(String.format(this.minText, this.minValue));
17460             return false;
17461         }
17462         if(num > this.maxValue){
17463             this.markInvalid(String.format(this.maxText, this.maxValue));
17464             return false;
17465         }
17466         return true;
17467     },
17468
17469     getValue : function(){
17470         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17471     },
17472
17473     // private
17474     parseValue : function(value){
17475         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17476         return isNaN(value) ? '' : value;
17477     },
17478
17479     // private
17480     fixPrecision : function(value){
17481         var nan = isNaN(value);
17482         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17483             return nan ? '' : value;
17484         }
17485         return parseFloat(value).toFixed(this.decimalPrecision);
17486     },
17487
17488     setValue : function(v){
17489         v = this.fixPrecision(v);
17490         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17491     },
17492
17493     // private
17494     decimalPrecisionFcn : function(v){
17495         return Math.floor(v);
17496     },
17497
17498     beforeBlur : function(){
17499         var v = this.parseValue(this.getRawValue());
17500         if(v){
17501             this.setValue(v);
17502         }
17503     }
17504 });/*
17505  * Based on:
17506  * Ext JS Library 1.1.1
17507  * Copyright(c) 2006-2007, Ext JS, LLC.
17508  *
17509  * Originally Released Under LGPL - original licence link has changed is not relivant.
17510  *
17511  * Fork - LGPL
17512  * <script type="text/javascript">
17513  */
17514  
17515 /**
17516  * @class Roo.form.DateField
17517  * @extends Roo.form.TriggerField
17518  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17519 * @constructor
17520 * Create a new DateField
17521 * @param {Object} config
17522  */
17523 Roo.form.DateField = function(config)
17524 {
17525     Roo.form.DateField.superclass.constructor.call(this, config);
17526     
17527       this.addEvents({
17528          
17529         /**
17530          * @event select
17531          * Fires when a date is selected
17532              * @param {Roo.form.DateField} combo This combo box
17533              * @param {Date} date The date selected
17534              */
17535         'select' : true
17536          
17537     });
17538     
17539     
17540     if(typeof this.minValue == "string") {
17541         this.minValue = this.parseDate(this.minValue);
17542     }
17543     if(typeof this.maxValue == "string") {
17544         this.maxValue = this.parseDate(this.maxValue);
17545     }
17546     this.ddMatch = null;
17547     if(this.disabledDates){
17548         var dd = this.disabledDates;
17549         var re = "(?:";
17550         for(var i = 0; i < dd.length; i++){
17551             re += dd[i];
17552             if(i != dd.length-1) {
17553                 re += "|";
17554             }
17555         }
17556         this.ddMatch = new RegExp(re + ")");
17557     }
17558 };
17559
17560 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17561     /**
17562      * @cfg {String} format
17563      * The default date format string which can be overriden for localization support.  The format must be
17564      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17565      */
17566     format : "m/d/y",
17567     /**
17568      * @cfg {String} altFormats
17569      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17570      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17571      */
17572     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17573     /**
17574      * @cfg {Array} disabledDays
17575      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17576      */
17577     disabledDays : null,
17578     /**
17579      * @cfg {String} disabledDaysText
17580      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17581      */
17582     disabledDaysText : "Disabled",
17583     /**
17584      * @cfg {Array} disabledDates
17585      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17586      * expression so they are very powerful. Some examples:
17587      * <ul>
17588      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17589      * <li>["03/08", "09/16"] would disable those days for every year</li>
17590      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17591      * <li>["03/../2006"] would disable every day in March 2006</li>
17592      * <li>["^03"] would disable every day in every March</li>
17593      * </ul>
17594      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17595      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17596      */
17597     disabledDates : null,
17598     /**
17599      * @cfg {String} disabledDatesText
17600      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17601      */
17602     disabledDatesText : "Disabled",
17603         
17604         
17605         /**
17606      * @cfg {Date/String} zeroValue
17607      * if the date is less that this number, then the field is rendered as empty
17608      * default is 1800
17609      */
17610         zeroValue : '1800-01-01',
17611         
17612         
17613     /**
17614      * @cfg {Date/String} minValue
17615      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17616      * valid format (defaults to null).
17617      */
17618     minValue : null,
17619     /**
17620      * @cfg {Date/String} maxValue
17621      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17622      * valid format (defaults to null).
17623      */
17624     maxValue : null,
17625     /**
17626      * @cfg {String} minText
17627      * The error text to display when the date in the cell is before minValue (defaults to
17628      * 'The date in this field must be after {minValue}').
17629      */
17630     minText : "The date in this field must be equal to or after {0}",
17631     /**
17632      * @cfg {String} maxText
17633      * The error text to display when the date in the cell is after maxValue (defaults to
17634      * 'The date in this field must be before {maxValue}').
17635      */
17636     maxText : "The date in this field must be equal to or before {0}",
17637     /**
17638      * @cfg {String} invalidText
17639      * The error text to display when the date in the field is invalid (defaults to
17640      * '{value} is not a valid date - it must be in the format {format}').
17641      */
17642     invalidText : "{0} is not a valid date - it must be in the format {1}",
17643     /**
17644      * @cfg {String} triggerClass
17645      * An additional CSS class used to style the trigger button.  The trigger will always get the
17646      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17647      * which displays a calendar icon).
17648      */
17649     triggerClass : 'x-form-date-trigger',
17650     
17651
17652     /**
17653      * @cfg {Boolean} useIso
17654      * if enabled, then the date field will use a hidden field to store the 
17655      * real value as iso formated date. default (false)
17656      */ 
17657     useIso : false,
17658     /**
17659      * @cfg {String/Object} autoCreate
17660      * A DomHelper element spec, or true for a default element spec (defaults to
17661      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17662      */ 
17663     // private
17664     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17665     
17666     // private
17667     hiddenField: false,
17668     
17669     onRender : function(ct, position)
17670     {
17671         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17672         if (this.useIso) {
17673             //this.el.dom.removeAttribute('name'); 
17674             Roo.log("Changing name?");
17675             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17676             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17677                     'before', true);
17678             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17679             // prevent input submission
17680             this.hiddenName = this.name;
17681         }
17682             
17683             
17684     },
17685     
17686     // private
17687     validateValue : function(value)
17688     {
17689         value = this.formatDate(value);
17690         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17691             Roo.log('super failed');
17692             return false;
17693         }
17694         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17695              return true;
17696         }
17697         var svalue = value;
17698         value = this.parseDate(value);
17699         if(!value){
17700             Roo.log('parse date failed' + svalue);
17701             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17702             return false;
17703         }
17704         var time = value.getTime();
17705         if(this.minValue && time < this.minValue.getTime()){
17706             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17707             return false;
17708         }
17709         if(this.maxValue && time > this.maxValue.getTime()){
17710             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17711             return false;
17712         }
17713         if(this.disabledDays){
17714             var day = value.getDay();
17715             for(var i = 0; i < this.disabledDays.length; i++) {
17716                 if(day === this.disabledDays[i]){
17717                     this.markInvalid(this.disabledDaysText);
17718                     return false;
17719                 }
17720             }
17721         }
17722         var fvalue = this.formatDate(value);
17723         if(this.ddMatch && this.ddMatch.test(fvalue)){
17724             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17725             return false;
17726         }
17727         return true;
17728     },
17729
17730     // private
17731     // Provides logic to override the default TriggerField.validateBlur which just returns true
17732     validateBlur : function(){
17733         return !this.menu || !this.menu.isVisible();
17734     },
17735     
17736     getName: function()
17737     {
17738         // returns hidden if it's set..
17739         if (!this.rendered) {return ''};
17740         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17741         
17742     },
17743
17744     /**
17745      * Returns the current date value of the date field.
17746      * @return {Date} The date value
17747      */
17748     getValue : function(){
17749         
17750         return  this.hiddenField ?
17751                 this.hiddenField.value :
17752                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17753     },
17754
17755     /**
17756      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17757      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17758      * (the default format used is "m/d/y").
17759      * <br />Usage:
17760      * <pre><code>
17761 //All of these calls set the same date value (May 4, 2006)
17762
17763 //Pass a date object:
17764 var dt = new Date('5/4/06');
17765 dateField.setValue(dt);
17766
17767 //Pass a date string (default format):
17768 dateField.setValue('5/4/06');
17769
17770 //Pass a date string (custom format):
17771 dateField.format = 'Y-m-d';
17772 dateField.setValue('2006-5-4');
17773 </code></pre>
17774      * @param {String/Date} date The date or valid date string
17775      */
17776     setValue : function(date){
17777         if (this.hiddenField) {
17778             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17779         }
17780         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17781         // make sure the value field is always stored as a date..
17782         this.value = this.parseDate(date);
17783         
17784         
17785     },
17786
17787     // private
17788     parseDate : function(value){
17789                 
17790                 if (value instanceof Date) {
17791                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17792                                 return  '';
17793                         }
17794                         return value;
17795                 }
17796                 
17797                 
17798         if(!value || value instanceof Date){
17799             return value;
17800         }
17801         var v = Date.parseDate(value, this.format);
17802          if (!v && this.useIso) {
17803             v = Date.parseDate(value, 'Y-m-d');
17804         }
17805         if(!v && this.altFormats){
17806             if(!this.altFormatsArray){
17807                 this.altFormatsArray = this.altFormats.split("|");
17808             }
17809             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17810                 v = Date.parseDate(value, this.altFormatsArray[i]);
17811             }
17812         }
17813                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17814                         v = '';
17815                 }
17816         return v;
17817     },
17818
17819     // private
17820     formatDate : function(date, fmt){
17821         return (!date || !(date instanceof Date)) ?
17822                date : date.dateFormat(fmt || this.format);
17823     },
17824
17825     // private
17826     menuListeners : {
17827         select: function(m, d){
17828             
17829             this.setValue(d);
17830             this.fireEvent('select', this, d);
17831         },
17832         show : function(){ // retain focus styling
17833             this.onFocus();
17834         },
17835         hide : function(){
17836             this.focus.defer(10, this);
17837             var ml = this.menuListeners;
17838             this.menu.un("select", ml.select,  this);
17839             this.menu.un("show", ml.show,  this);
17840             this.menu.un("hide", ml.hide,  this);
17841         }
17842     },
17843
17844     // private
17845     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17846     onTriggerClick : function(){
17847         if(this.disabled){
17848             return;
17849         }
17850         if(this.menu == null){
17851             this.menu = new Roo.menu.DateMenu();
17852         }
17853         Roo.apply(this.menu.picker,  {
17854             showClear: this.allowBlank,
17855             minDate : this.minValue,
17856             maxDate : this.maxValue,
17857             disabledDatesRE : this.ddMatch,
17858             disabledDatesText : this.disabledDatesText,
17859             disabledDays : this.disabledDays,
17860             disabledDaysText : this.disabledDaysText,
17861             format : this.useIso ? 'Y-m-d' : this.format,
17862             minText : String.format(this.minText, this.formatDate(this.minValue)),
17863             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17864         });
17865         this.menu.on(Roo.apply({}, this.menuListeners, {
17866             scope:this
17867         }));
17868         this.menu.picker.setValue(this.getValue() || new Date());
17869         this.menu.show(this.el, "tl-bl?");
17870     },
17871
17872     beforeBlur : function(){
17873         var v = this.parseDate(this.getRawValue());
17874         if(v){
17875             this.setValue(v);
17876         }
17877     },
17878
17879     /*@
17880      * overide
17881      * 
17882      */
17883     isDirty : function() {
17884         if(this.disabled) {
17885             return false;
17886         }
17887         
17888         if(typeof(this.startValue) === 'undefined'){
17889             return false;
17890         }
17891         
17892         return String(this.getValue()) !== String(this.startValue);
17893         
17894     },
17895     // @overide
17896     cleanLeadingSpace : function(e)
17897     {
17898        return;
17899     }
17900     
17901 });/*
17902  * Based on:
17903  * Ext JS Library 1.1.1
17904  * Copyright(c) 2006-2007, Ext JS, LLC.
17905  *
17906  * Originally Released Under LGPL - original licence link has changed is not relivant.
17907  *
17908  * Fork - LGPL
17909  * <script type="text/javascript">
17910  */
17911  
17912 /**
17913  * @class Roo.form.MonthField
17914  * @extends Roo.form.TriggerField
17915  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17916 * @constructor
17917 * Create a new MonthField
17918 * @param {Object} config
17919  */
17920 Roo.form.MonthField = function(config){
17921     
17922     Roo.form.MonthField.superclass.constructor.call(this, config);
17923     
17924       this.addEvents({
17925          
17926         /**
17927          * @event select
17928          * Fires when a date is selected
17929              * @param {Roo.form.MonthFieeld} combo This combo box
17930              * @param {Date} date The date selected
17931              */
17932         'select' : true
17933          
17934     });
17935     
17936     
17937     if(typeof this.minValue == "string") {
17938         this.minValue = this.parseDate(this.minValue);
17939     }
17940     if(typeof this.maxValue == "string") {
17941         this.maxValue = this.parseDate(this.maxValue);
17942     }
17943     this.ddMatch = null;
17944     if(this.disabledDates){
17945         var dd = this.disabledDates;
17946         var re = "(?:";
17947         for(var i = 0; i < dd.length; i++){
17948             re += dd[i];
17949             if(i != dd.length-1) {
17950                 re += "|";
17951             }
17952         }
17953         this.ddMatch = new RegExp(re + ")");
17954     }
17955 };
17956
17957 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17958     /**
17959      * @cfg {String} format
17960      * The default date format string which can be overriden for localization support.  The format must be
17961      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17962      */
17963     format : "M Y",
17964     /**
17965      * @cfg {String} altFormats
17966      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17967      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17968      */
17969     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17970     /**
17971      * @cfg {Array} disabledDays
17972      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17973      */
17974     disabledDays : [0,1,2,3,4,5,6],
17975     /**
17976      * @cfg {String} disabledDaysText
17977      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17978      */
17979     disabledDaysText : "Disabled",
17980     /**
17981      * @cfg {Array} disabledDates
17982      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17983      * expression so they are very powerful. Some examples:
17984      * <ul>
17985      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17986      * <li>["03/08", "09/16"] would disable those days for every year</li>
17987      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17988      * <li>["03/../2006"] would disable every day in March 2006</li>
17989      * <li>["^03"] would disable every day in every March</li>
17990      * </ul>
17991      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17992      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17993      */
17994     disabledDates : null,
17995     /**
17996      * @cfg {String} disabledDatesText
17997      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17998      */
17999     disabledDatesText : "Disabled",
18000     /**
18001      * @cfg {Date/String} minValue
18002      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18003      * valid format (defaults to null).
18004      */
18005     minValue : null,
18006     /**
18007      * @cfg {Date/String} maxValue
18008      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18009      * valid format (defaults to null).
18010      */
18011     maxValue : null,
18012     /**
18013      * @cfg {String} minText
18014      * The error text to display when the date in the cell is before minValue (defaults to
18015      * 'The date in this field must be after {minValue}').
18016      */
18017     minText : "The date in this field must be equal to or after {0}",
18018     /**
18019      * @cfg {String} maxTextf
18020      * The error text to display when the date in the cell is after maxValue (defaults to
18021      * 'The date in this field must be before {maxValue}').
18022      */
18023     maxText : "The date in this field must be equal to or before {0}",
18024     /**
18025      * @cfg {String} invalidText
18026      * The error text to display when the date in the field is invalid (defaults to
18027      * '{value} is not a valid date - it must be in the format {format}').
18028      */
18029     invalidText : "{0} is not a valid date - it must be in the format {1}",
18030     /**
18031      * @cfg {String} triggerClass
18032      * An additional CSS class used to style the trigger button.  The trigger will always get the
18033      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18034      * which displays a calendar icon).
18035      */
18036     triggerClass : 'x-form-date-trigger',
18037     
18038
18039     /**
18040      * @cfg {Boolean} useIso
18041      * if enabled, then the date field will use a hidden field to store the 
18042      * real value as iso formated date. default (true)
18043      */ 
18044     useIso : true,
18045     /**
18046      * @cfg {String/Object} autoCreate
18047      * A DomHelper element spec, or true for a default element spec (defaults to
18048      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18049      */ 
18050     // private
18051     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18052     
18053     // private
18054     hiddenField: false,
18055     
18056     hideMonthPicker : false,
18057     
18058     onRender : function(ct, position)
18059     {
18060         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18061         if (this.useIso) {
18062             this.el.dom.removeAttribute('name'); 
18063             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18064                     'before', true);
18065             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18066             // prevent input submission
18067             this.hiddenName = this.name;
18068         }
18069             
18070             
18071     },
18072     
18073     // private
18074     validateValue : function(value)
18075     {
18076         value = this.formatDate(value);
18077         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18078             return false;
18079         }
18080         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18081              return true;
18082         }
18083         var svalue = value;
18084         value = this.parseDate(value);
18085         if(!value){
18086             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18087             return false;
18088         }
18089         var time = value.getTime();
18090         if(this.minValue && time < this.minValue.getTime()){
18091             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18092             return false;
18093         }
18094         if(this.maxValue && time > this.maxValue.getTime()){
18095             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18096             return false;
18097         }
18098         /*if(this.disabledDays){
18099             var day = value.getDay();
18100             for(var i = 0; i < this.disabledDays.length; i++) {
18101                 if(day === this.disabledDays[i]){
18102                     this.markInvalid(this.disabledDaysText);
18103                     return false;
18104                 }
18105             }
18106         }
18107         */
18108         var fvalue = this.formatDate(value);
18109         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18110             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18111             return false;
18112         }
18113         */
18114         return true;
18115     },
18116
18117     // private
18118     // Provides logic to override the default TriggerField.validateBlur which just returns true
18119     validateBlur : function(){
18120         return !this.menu || !this.menu.isVisible();
18121     },
18122
18123     /**
18124      * Returns the current date value of the date field.
18125      * @return {Date} The date value
18126      */
18127     getValue : function(){
18128         
18129         
18130         
18131         return  this.hiddenField ?
18132                 this.hiddenField.value :
18133                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18134     },
18135
18136     /**
18137      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18138      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18139      * (the default format used is "m/d/y").
18140      * <br />Usage:
18141      * <pre><code>
18142 //All of these calls set the same date value (May 4, 2006)
18143
18144 //Pass a date object:
18145 var dt = new Date('5/4/06');
18146 monthField.setValue(dt);
18147
18148 //Pass a date string (default format):
18149 monthField.setValue('5/4/06');
18150
18151 //Pass a date string (custom format):
18152 monthField.format = 'Y-m-d';
18153 monthField.setValue('2006-5-4');
18154 </code></pre>
18155      * @param {String/Date} date The date or valid date string
18156      */
18157     setValue : function(date){
18158         Roo.log('month setValue' + date);
18159         // can only be first of month..
18160         
18161         var val = this.parseDate(date);
18162         
18163         if (this.hiddenField) {
18164             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18165         }
18166         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18167         this.value = this.parseDate(date);
18168     },
18169
18170     // private
18171     parseDate : function(value){
18172         if(!value || value instanceof Date){
18173             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18174             return value;
18175         }
18176         var v = Date.parseDate(value, this.format);
18177         if (!v && this.useIso) {
18178             v = Date.parseDate(value, 'Y-m-d');
18179         }
18180         if (v) {
18181             // 
18182             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18183         }
18184         
18185         
18186         if(!v && this.altFormats){
18187             if(!this.altFormatsArray){
18188                 this.altFormatsArray = this.altFormats.split("|");
18189             }
18190             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18191                 v = Date.parseDate(value, this.altFormatsArray[i]);
18192             }
18193         }
18194         return v;
18195     },
18196
18197     // private
18198     formatDate : function(date, fmt){
18199         return (!date || !(date instanceof Date)) ?
18200                date : date.dateFormat(fmt || this.format);
18201     },
18202
18203     // private
18204     menuListeners : {
18205         select: function(m, d){
18206             this.setValue(d);
18207             this.fireEvent('select', this, d);
18208         },
18209         show : function(){ // retain focus styling
18210             this.onFocus();
18211         },
18212         hide : function(){
18213             this.focus.defer(10, this);
18214             var ml = this.menuListeners;
18215             this.menu.un("select", ml.select,  this);
18216             this.menu.un("show", ml.show,  this);
18217             this.menu.un("hide", ml.hide,  this);
18218         }
18219     },
18220     // private
18221     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18222     onTriggerClick : function(){
18223         if(this.disabled){
18224             return;
18225         }
18226         if(this.menu == null){
18227             this.menu = new Roo.menu.DateMenu();
18228            
18229         }
18230         
18231         Roo.apply(this.menu.picker,  {
18232             
18233             showClear: this.allowBlank,
18234             minDate : this.minValue,
18235             maxDate : this.maxValue,
18236             disabledDatesRE : this.ddMatch,
18237             disabledDatesText : this.disabledDatesText,
18238             
18239             format : this.useIso ? 'Y-m-d' : this.format,
18240             minText : String.format(this.minText, this.formatDate(this.minValue)),
18241             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18242             
18243         });
18244          this.menu.on(Roo.apply({}, this.menuListeners, {
18245             scope:this
18246         }));
18247        
18248         
18249         var m = this.menu;
18250         var p = m.picker;
18251         
18252         // hide month picker get's called when we called by 'before hide';
18253         
18254         var ignorehide = true;
18255         p.hideMonthPicker  = function(disableAnim){
18256             if (ignorehide) {
18257                 return;
18258             }
18259              if(this.monthPicker){
18260                 Roo.log("hideMonthPicker called");
18261                 if(disableAnim === true){
18262                     this.monthPicker.hide();
18263                 }else{
18264                     this.monthPicker.slideOut('t', {duration:.2});
18265                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18266                     p.fireEvent("select", this, this.value);
18267                     m.hide();
18268                 }
18269             }
18270         }
18271         
18272         Roo.log('picker set value');
18273         Roo.log(this.getValue());
18274         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18275         m.show(this.el, 'tl-bl?');
18276         ignorehide  = false;
18277         // this will trigger hideMonthPicker..
18278         
18279         
18280         // hidden the day picker
18281         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18282         
18283         
18284         
18285       
18286         
18287         p.showMonthPicker.defer(100, p);
18288     
18289         
18290        
18291     },
18292
18293     beforeBlur : function(){
18294         var v = this.parseDate(this.getRawValue());
18295         if(v){
18296             this.setValue(v);
18297         }
18298     }
18299
18300     /** @cfg {Boolean} grow @hide */
18301     /** @cfg {Number} growMin @hide */
18302     /** @cfg {Number} growMax @hide */
18303     /**
18304      * @hide
18305      * @method autoSize
18306      */
18307 });/*
18308  * Based on:
18309  * Ext JS Library 1.1.1
18310  * Copyright(c) 2006-2007, Ext JS, LLC.
18311  *
18312  * Originally Released Under LGPL - original licence link has changed is not relivant.
18313  *
18314  * Fork - LGPL
18315  * <script type="text/javascript">
18316  */
18317  
18318
18319 /**
18320  * @class Roo.form.ComboBox
18321  * @extends Roo.form.TriggerField
18322  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18323  * @constructor
18324  * Create a new ComboBox.
18325  * @param {Object} config Configuration options
18326  */
18327 Roo.form.ComboBox = function(config){
18328     Roo.form.ComboBox.superclass.constructor.call(this, config);
18329     this.addEvents({
18330         /**
18331          * @event expand
18332          * Fires when the dropdown list is expanded
18333              * @param {Roo.form.ComboBox} combo This combo box
18334              */
18335         'expand' : true,
18336         /**
18337          * @event collapse
18338          * Fires when the dropdown list is collapsed
18339              * @param {Roo.form.ComboBox} combo This combo box
18340              */
18341         'collapse' : true,
18342         /**
18343          * @event beforeselect
18344          * Fires before a list item is selected. Return false to cancel the selection.
18345              * @param {Roo.form.ComboBox} combo This combo box
18346              * @param {Roo.data.Record} record The data record returned from the underlying store
18347              * @param {Number} index The index of the selected item in the dropdown list
18348              */
18349         'beforeselect' : true,
18350         /**
18351          * @event select
18352          * Fires when a list item is selected
18353              * @param {Roo.form.ComboBox} combo This combo box
18354              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18355              * @param {Number} index The index of the selected item in the dropdown list
18356              */
18357         'select' : true,
18358         /**
18359          * @event beforequery
18360          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18361          * The event object passed has these properties:
18362              * @param {Roo.form.ComboBox} combo This combo box
18363              * @param {String} query The query
18364              * @param {Boolean} forceAll true to force "all" query
18365              * @param {Boolean} cancel true to cancel the query
18366              * @param {Object} e The query event object
18367              */
18368         'beforequery': true,
18369          /**
18370          * @event add
18371          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18372              * @param {Roo.form.ComboBox} combo This combo box
18373              */
18374         'add' : true,
18375         /**
18376          * @event edit
18377          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18378              * @param {Roo.form.ComboBox} combo This combo box
18379              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18380              */
18381         'edit' : true
18382         
18383         
18384     });
18385     if(this.transform){
18386         this.allowDomMove = false;
18387         var s = Roo.getDom(this.transform);
18388         if(!this.hiddenName){
18389             this.hiddenName = s.name;
18390         }
18391         if(!this.store){
18392             this.mode = 'local';
18393             var d = [], opts = s.options;
18394             for(var i = 0, len = opts.length;i < len; i++){
18395                 var o = opts[i];
18396                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18397                 if(o.selected) {
18398                     this.value = value;
18399                 }
18400                 d.push([value, o.text]);
18401             }
18402             this.store = new Roo.data.SimpleStore({
18403                 'id': 0,
18404                 fields: ['value', 'text'],
18405                 data : d
18406             });
18407             this.valueField = 'value';
18408             this.displayField = 'text';
18409         }
18410         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18411         if(!this.lazyRender){
18412             this.target = true;
18413             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18414             s.parentNode.removeChild(s); // remove it
18415             this.render(this.el.parentNode);
18416         }else{
18417             s.parentNode.removeChild(s); // remove it
18418         }
18419
18420     }
18421     if (this.store) {
18422         this.store = Roo.factory(this.store, Roo.data);
18423     }
18424     
18425     this.selectedIndex = -1;
18426     if(this.mode == 'local'){
18427         if(config.queryDelay === undefined){
18428             this.queryDelay = 10;
18429         }
18430         if(config.minChars === undefined){
18431             this.minChars = 0;
18432         }
18433     }
18434 };
18435
18436 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18437     /**
18438      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18439      */
18440     /**
18441      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18442      * rendering into an Roo.Editor, defaults to false)
18443      */
18444     /**
18445      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18446      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18447      */
18448     /**
18449      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18450      */
18451     /**
18452      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18453      * the dropdown list (defaults to undefined, with no header element)
18454      */
18455
18456      /**
18457      * @cfg {String/Roo.Template} tpl The template to use to render the output
18458      */
18459      
18460     // private
18461     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18462     /**
18463      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18464      */
18465     listWidth: undefined,
18466     /**
18467      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18468      * mode = 'remote' or 'text' if mode = 'local')
18469      */
18470     displayField: undefined,
18471     /**
18472      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18473      * mode = 'remote' or 'value' if mode = 'local'). 
18474      * Note: use of a valueField requires the user make a selection
18475      * in order for a value to be mapped.
18476      */
18477     valueField: undefined,
18478     
18479     
18480     /**
18481      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18482      * field's data value (defaults to the underlying DOM element's name)
18483      */
18484     hiddenName: undefined,
18485     /**
18486      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18487      */
18488     listClass: '',
18489     /**
18490      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18491      */
18492     selectedClass: 'x-combo-selected',
18493     /**
18494      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18495      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18496      * which displays a downward arrow icon).
18497      */
18498     triggerClass : 'x-form-arrow-trigger',
18499     /**
18500      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18501      */
18502     shadow:'sides',
18503     /**
18504      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18505      * anchor positions (defaults to 'tl-bl')
18506      */
18507     listAlign: 'tl-bl?',
18508     /**
18509      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18510      */
18511     maxHeight: 300,
18512     /**
18513      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18514      * query specified by the allQuery config option (defaults to 'query')
18515      */
18516     triggerAction: 'query',
18517     /**
18518      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18519      * (defaults to 4, does not apply if editable = false)
18520      */
18521     minChars : 4,
18522     /**
18523      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18524      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18525      */
18526     typeAhead: false,
18527     /**
18528      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18529      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18530      */
18531     queryDelay: 500,
18532     /**
18533      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18534      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18535      */
18536     pageSize: 0,
18537     /**
18538      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18539      * when editable = true (defaults to false)
18540      */
18541     selectOnFocus:false,
18542     /**
18543      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18544      */
18545     queryParam: 'query',
18546     /**
18547      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18548      * when mode = 'remote' (defaults to 'Loading...')
18549      */
18550     loadingText: 'Loading...',
18551     /**
18552      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18553      */
18554     resizable: false,
18555     /**
18556      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18557      */
18558     handleHeight : 8,
18559     /**
18560      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18561      * traditional select (defaults to true)
18562      */
18563     editable: true,
18564     /**
18565      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18566      */
18567     allQuery: '',
18568     /**
18569      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18570      */
18571     mode: 'remote',
18572     /**
18573      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18574      * listWidth has a higher value)
18575      */
18576     minListWidth : 70,
18577     /**
18578      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18579      * allow the user to set arbitrary text into the field (defaults to false)
18580      */
18581     forceSelection:false,
18582     /**
18583      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18584      * if typeAhead = true (defaults to 250)
18585      */
18586     typeAheadDelay : 250,
18587     /**
18588      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18589      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18590      */
18591     valueNotFoundText : undefined,
18592     /**
18593      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18594      */
18595     blockFocus : false,
18596     
18597     /**
18598      * @cfg {Boolean} disableClear Disable showing of clear button.
18599      */
18600     disableClear : false,
18601     /**
18602      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18603      */
18604     alwaysQuery : false,
18605     
18606     //private
18607     addicon : false,
18608     editicon: false,
18609     
18610     // element that contains real text value.. (when hidden is used..)
18611      
18612     // private
18613     onRender : function(ct, position)
18614     {
18615         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18616         
18617         if(this.hiddenName){
18618             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18619                     'before', true);
18620             this.hiddenField.value =
18621                 this.hiddenValue !== undefined ? this.hiddenValue :
18622                 this.value !== undefined ? this.value : '';
18623
18624             // prevent input submission
18625             this.el.dom.removeAttribute('name');
18626              
18627              
18628         }
18629         
18630         if(Roo.isGecko){
18631             this.el.dom.setAttribute('autocomplete', 'off');
18632         }
18633
18634         var cls = 'x-combo-list';
18635
18636         this.list = new Roo.Layer({
18637             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18638         });
18639
18640         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18641         this.list.setWidth(lw);
18642         this.list.swallowEvent('mousewheel');
18643         this.assetHeight = 0;
18644
18645         if(this.title){
18646             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18647             this.assetHeight += this.header.getHeight();
18648         }
18649
18650         this.innerList = this.list.createChild({cls:cls+'-inner'});
18651         this.innerList.on('mouseover', this.onViewOver, this);
18652         this.innerList.on('mousemove', this.onViewMove, this);
18653         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18654         
18655         if(this.allowBlank && !this.pageSize && !this.disableClear){
18656             this.footer = this.list.createChild({cls:cls+'-ft'});
18657             this.pageTb = new Roo.Toolbar(this.footer);
18658            
18659         }
18660         if(this.pageSize){
18661             this.footer = this.list.createChild({cls:cls+'-ft'});
18662             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18663                     {pageSize: this.pageSize});
18664             
18665         }
18666         
18667         if (this.pageTb && this.allowBlank && !this.disableClear) {
18668             var _this = this;
18669             this.pageTb.add(new Roo.Toolbar.Fill(), {
18670                 cls: 'x-btn-icon x-btn-clear',
18671                 text: '&#160;',
18672                 handler: function()
18673                 {
18674                     _this.collapse();
18675                     _this.clearValue();
18676                     _this.onSelect(false, -1);
18677                 }
18678             });
18679         }
18680         if (this.footer) {
18681             this.assetHeight += this.footer.getHeight();
18682         }
18683         
18684
18685         if(!this.tpl){
18686             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18687         }
18688
18689         this.view = new Roo.View(this.innerList, this.tpl, {
18690             singleSelect:true,
18691             store: this.store,
18692             selectedClass: this.selectedClass
18693         });
18694
18695         this.view.on('click', this.onViewClick, this);
18696
18697         this.store.on('beforeload', this.onBeforeLoad, this);
18698         this.store.on('load', this.onLoad, this);
18699         this.store.on('loadexception', this.onLoadException, this);
18700
18701         if(this.resizable){
18702             this.resizer = new Roo.Resizable(this.list,  {
18703                pinned:true, handles:'se'
18704             });
18705             this.resizer.on('resize', function(r, w, h){
18706                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18707                 this.listWidth = w;
18708                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18709                 this.restrictHeight();
18710             }, this);
18711             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18712         }
18713         if(!this.editable){
18714             this.editable = true;
18715             this.setEditable(false);
18716         }  
18717         
18718         
18719         if (typeof(this.events.add.listeners) != 'undefined') {
18720             
18721             this.addicon = this.wrap.createChild(
18722                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18723        
18724             this.addicon.on('click', function(e) {
18725                 this.fireEvent('add', this);
18726             }, this);
18727         }
18728         if (typeof(this.events.edit.listeners) != 'undefined') {
18729             
18730             this.editicon = this.wrap.createChild(
18731                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18732             if (this.addicon) {
18733                 this.editicon.setStyle('margin-left', '40px');
18734             }
18735             this.editicon.on('click', function(e) {
18736                 
18737                 // we fire even  if inothing is selected..
18738                 this.fireEvent('edit', this, this.lastData );
18739                 
18740             }, this);
18741         }
18742         
18743         
18744         
18745     },
18746
18747     // private
18748     initEvents : function(){
18749         Roo.form.ComboBox.superclass.initEvents.call(this);
18750
18751         this.keyNav = new Roo.KeyNav(this.el, {
18752             "up" : function(e){
18753                 this.inKeyMode = true;
18754                 this.selectPrev();
18755             },
18756
18757             "down" : function(e){
18758                 if(!this.isExpanded()){
18759                     this.onTriggerClick();
18760                 }else{
18761                     this.inKeyMode = true;
18762                     this.selectNext();
18763                 }
18764             },
18765
18766             "enter" : function(e){
18767                 this.onViewClick();
18768                 //return true;
18769             },
18770
18771             "esc" : function(e){
18772                 this.collapse();
18773             },
18774
18775             "tab" : function(e){
18776                 this.onViewClick(false);
18777                 this.fireEvent("specialkey", this, e);
18778                 return true;
18779             },
18780
18781             scope : this,
18782
18783             doRelay : function(foo, bar, hname){
18784                 if(hname == 'down' || this.scope.isExpanded()){
18785                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18786                 }
18787                 return true;
18788             },
18789
18790             forceKeyDown: true
18791         });
18792         this.queryDelay = Math.max(this.queryDelay || 10,
18793                 this.mode == 'local' ? 10 : 250);
18794         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18795         if(this.typeAhead){
18796             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18797         }
18798         if(this.editable !== false){
18799             this.el.on("keyup", this.onKeyUp, this);
18800         }
18801         if(this.forceSelection){
18802             this.on('blur', this.doForce, this);
18803         }
18804     },
18805
18806     onDestroy : function(){
18807         if(this.view){
18808             this.view.setStore(null);
18809             this.view.el.removeAllListeners();
18810             this.view.el.remove();
18811             this.view.purgeListeners();
18812         }
18813         if(this.list){
18814             this.list.destroy();
18815         }
18816         if(this.store){
18817             this.store.un('beforeload', this.onBeforeLoad, this);
18818             this.store.un('load', this.onLoad, this);
18819             this.store.un('loadexception', this.onLoadException, this);
18820         }
18821         Roo.form.ComboBox.superclass.onDestroy.call(this);
18822     },
18823
18824     // private
18825     fireKey : function(e){
18826         if(e.isNavKeyPress() && !this.list.isVisible()){
18827             this.fireEvent("specialkey", this, e);
18828         }
18829     },
18830
18831     // private
18832     onResize: function(w, h){
18833         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18834         
18835         if(typeof w != 'number'){
18836             // we do not handle it!?!?
18837             return;
18838         }
18839         var tw = this.trigger.getWidth();
18840         tw += this.addicon ? this.addicon.getWidth() : 0;
18841         tw += this.editicon ? this.editicon.getWidth() : 0;
18842         var x = w - tw;
18843         this.el.setWidth( this.adjustWidth('input', x));
18844             
18845         this.trigger.setStyle('left', x+'px');
18846         
18847         if(this.list && this.listWidth === undefined){
18848             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18849             this.list.setWidth(lw);
18850             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18851         }
18852         
18853     
18854         
18855     },
18856
18857     /**
18858      * Allow or prevent the user from directly editing the field text.  If false is passed,
18859      * the user will only be able to select from the items defined in the dropdown list.  This method
18860      * is the runtime equivalent of setting the 'editable' config option at config time.
18861      * @param {Boolean} value True to allow the user to directly edit the field text
18862      */
18863     setEditable : function(value){
18864         if(value == this.editable){
18865             return;
18866         }
18867         this.editable = value;
18868         if(!value){
18869             this.el.dom.setAttribute('readOnly', true);
18870             this.el.on('mousedown', this.onTriggerClick,  this);
18871             this.el.addClass('x-combo-noedit');
18872         }else{
18873             this.el.dom.setAttribute('readOnly', false);
18874             this.el.un('mousedown', this.onTriggerClick,  this);
18875             this.el.removeClass('x-combo-noedit');
18876         }
18877     },
18878
18879     // private
18880     onBeforeLoad : function(){
18881         if(!this.hasFocus){
18882             return;
18883         }
18884         this.innerList.update(this.loadingText ?
18885                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18886         this.restrictHeight();
18887         this.selectedIndex = -1;
18888     },
18889
18890     // private
18891     onLoad : function(){
18892         if(!this.hasFocus){
18893             return;
18894         }
18895         if(this.store.getCount() > 0){
18896             this.expand();
18897             this.restrictHeight();
18898             if(this.lastQuery == this.allQuery){
18899                 if(this.editable){
18900                     this.el.dom.select();
18901                 }
18902                 if(!this.selectByValue(this.value, true)){
18903                     this.select(0, true);
18904                 }
18905             }else{
18906                 this.selectNext();
18907                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18908                     this.taTask.delay(this.typeAheadDelay);
18909                 }
18910             }
18911         }else{
18912             this.onEmptyResults();
18913         }
18914         //this.el.focus();
18915     },
18916     // private
18917     onLoadException : function()
18918     {
18919         this.collapse();
18920         Roo.log(this.store.reader.jsonData);
18921         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18922             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18923         }
18924         
18925         
18926     },
18927     // private
18928     onTypeAhead : function(){
18929         if(this.store.getCount() > 0){
18930             var r = this.store.getAt(0);
18931             var newValue = r.data[this.displayField];
18932             var len = newValue.length;
18933             var selStart = this.getRawValue().length;
18934             if(selStart != len){
18935                 this.setRawValue(newValue);
18936                 this.selectText(selStart, newValue.length);
18937             }
18938         }
18939     },
18940
18941     // private
18942     onSelect : function(record, index){
18943         if(this.fireEvent('beforeselect', this, record, index) !== false){
18944             this.setFromData(index > -1 ? record.data : false);
18945             this.collapse();
18946             this.fireEvent('select', this, record, index);
18947         }
18948     },
18949
18950     /**
18951      * Returns the currently selected field value or empty string if no value is set.
18952      * @return {String} value The selected value
18953      */
18954     getValue : function(){
18955         if(this.valueField){
18956             return typeof this.value != 'undefined' ? this.value : '';
18957         }
18958         return Roo.form.ComboBox.superclass.getValue.call(this);
18959     },
18960
18961     /**
18962      * Clears any text/value currently set in the field
18963      */
18964     clearValue : function(){
18965         if(this.hiddenField){
18966             this.hiddenField.value = '';
18967         }
18968         this.value = '';
18969         this.setRawValue('');
18970         this.lastSelectionText = '';
18971         
18972     },
18973
18974     /**
18975      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18976      * will be displayed in the field.  If the value does not match the data value of an existing item,
18977      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18978      * Otherwise the field will be blank (although the value will still be set).
18979      * @param {String} value The value to match
18980      */
18981     setValue : function(v){
18982         var text = v;
18983         if(this.valueField){
18984             var r = this.findRecord(this.valueField, v);
18985             if(r){
18986                 text = r.data[this.displayField];
18987             }else if(this.valueNotFoundText !== undefined){
18988                 text = this.valueNotFoundText;
18989             }
18990         }
18991         this.lastSelectionText = text;
18992         if(this.hiddenField){
18993             this.hiddenField.value = v;
18994         }
18995         Roo.form.ComboBox.superclass.setValue.call(this, text);
18996         this.value = v;
18997     },
18998     /**
18999      * @property {Object} the last set data for the element
19000      */
19001     
19002     lastData : false,
19003     /**
19004      * Sets the value of the field based on a object which is related to the record format for the store.
19005      * @param {Object} value the value to set as. or false on reset?
19006      */
19007     setFromData : function(o){
19008         var dv = ''; // display value
19009         var vv = ''; // value value..
19010         this.lastData = o;
19011         if (this.displayField) {
19012             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19013         } else {
19014             // this is an error condition!!!
19015             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19016         }
19017         
19018         if(this.valueField){
19019             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19020         }
19021         if(this.hiddenField){
19022             this.hiddenField.value = vv;
19023             
19024             this.lastSelectionText = dv;
19025             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19026             this.value = vv;
19027             return;
19028         }
19029         // no hidden field.. - we store the value in 'value', but still display
19030         // display field!!!!
19031         this.lastSelectionText = dv;
19032         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19033         this.value = vv;
19034         
19035         
19036     },
19037     // private
19038     reset : function(){
19039         // overridden so that last data is reset..
19040         this.setValue(this.resetValue);
19041         this.originalValue = this.getValue();
19042         this.clearInvalid();
19043         this.lastData = false;
19044         if (this.view) {
19045             this.view.clearSelections();
19046         }
19047     },
19048     // private
19049     findRecord : function(prop, value){
19050         var record;
19051         if(this.store.getCount() > 0){
19052             this.store.each(function(r){
19053                 if(r.data[prop] == value){
19054                     record = r;
19055                     return false;
19056                 }
19057                 return true;
19058             });
19059         }
19060         return record;
19061     },
19062     
19063     getName: function()
19064     {
19065         // returns hidden if it's set..
19066         if (!this.rendered) {return ''};
19067         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19068         
19069     },
19070     // private
19071     onViewMove : function(e, t){
19072         this.inKeyMode = false;
19073     },
19074
19075     // private
19076     onViewOver : function(e, t){
19077         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19078             return;
19079         }
19080         var item = this.view.findItemFromChild(t);
19081         if(item){
19082             var index = this.view.indexOf(item);
19083             this.select(index, false);
19084         }
19085     },
19086
19087     // private
19088     onViewClick : function(doFocus)
19089     {
19090         var index = this.view.getSelectedIndexes()[0];
19091         var r = this.store.getAt(index);
19092         if(r){
19093             this.onSelect(r, index);
19094         }
19095         if(doFocus !== false && !this.blockFocus){
19096             this.el.focus();
19097         }
19098     },
19099
19100     // private
19101     restrictHeight : function(){
19102         this.innerList.dom.style.height = '';
19103         var inner = this.innerList.dom;
19104         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19105         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19106         this.list.beginUpdate();
19107         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19108         this.list.alignTo(this.el, this.listAlign);
19109         this.list.endUpdate();
19110     },
19111
19112     // private
19113     onEmptyResults : function(){
19114         this.collapse();
19115     },
19116
19117     /**
19118      * Returns true if the dropdown list is expanded, else false.
19119      */
19120     isExpanded : function(){
19121         return this.list.isVisible();
19122     },
19123
19124     /**
19125      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19126      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19127      * @param {String} value The data value of the item to select
19128      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19129      * selected item if it is not currently in view (defaults to true)
19130      * @return {Boolean} True if the value matched an item in the list, else false
19131      */
19132     selectByValue : function(v, scrollIntoView){
19133         if(v !== undefined && v !== null){
19134             var r = this.findRecord(this.valueField || this.displayField, v);
19135             if(r){
19136                 this.select(this.store.indexOf(r), scrollIntoView);
19137                 return true;
19138             }
19139         }
19140         return false;
19141     },
19142
19143     /**
19144      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19145      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19146      * @param {Number} index The zero-based index of the list item to select
19147      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19148      * selected item if it is not currently in view (defaults to true)
19149      */
19150     select : function(index, scrollIntoView){
19151         this.selectedIndex = index;
19152         this.view.select(index);
19153         if(scrollIntoView !== false){
19154             var el = this.view.getNode(index);
19155             if(el){
19156                 this.innerList.scrollChildIntoView(el, false);
19157             }
19158         }
19159     },
19160
19161     // private
19162     selectNext : function(){
19163         var ct = this.store.getCount();
19164         if(ct > 0){
19165             if(this.selectedIndex == -1){
19166                 this.select(0);
19167             }else if(this.selectedIndex < ct-1){
19168                 this.select(this.selectedIndex+1);
19169             }
19170         }
19171     },
19172
19173     // private
19174     selectPrev : function(){
19175         var ct = this.store.getCount();
19176         if(ct > 0){
19177             if(this.selectedIndex == -1){
19178                 this.select(0);
19179             }else if(this.selectedIndex != 0){
19180                 this.select(this.selectedIndex-1);
19181             }
19182         }
19183     },
19184
19185     // private
19186     onKeyUp : function(e){
19187         if(this.editable !== false && !e.isSpecialKey()){
19188             this.lastKey = e.getKey();
19189             this.dqTask.delay(this.queryDelay);
19190         }
19191     },
19192
19193     // private
19194     validateBlur : function(){
19195         return !this.list || !this.list.isVisible();   
19196     },
19197
19198     // private
19199     initQuery : function(){
19200         this.doQuery(this.getRawValue());
19201     },
19202
19203     // private
19204     doForce : function(){
19205         if(this.el.dom.value.length > 0){
19206             this.el.dom.value =
19207                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19208              
19209         }
19210     },
19211
19212     /**
19213      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19214      * query allowing the query action to be canceled if needed.
19215      * @param {String} query The SQL query to execute
19216      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19217      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19218      * saved in the current store (defaults to false)
19219      */
19220     doQuery : function(q, forceAll){
19221         if(q === undefined || q === null){
19222             q = '';
19223         }
19224         var qe = {
19225             query: q,
19226             forceAll: forceAll,
19227             combo: this,
19228             cancel:false
19229         };
19230         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19231             return false;
19232         }
19233         q = qe.query;
19234         forceAll = qe.forceAll;
19235         if(forceAll === true || (q.length >= this.minChars)){
19236             if(this.lastQuery != q || this.alwaysQuery){
19237                 this.lastQuery = q;
19238                 if(this.mode == 'local'){
19239                     this.selectedIndex = -1;
19240                     if(forceAll){
19241                         this.store.clearFilter();
19242                     }else{
19243                         this.store.filter(this.displayField, q);
19244                     }
19245                     this.onLoad();
19246                 }else{
19247                     this.store.baseParams[this.queryParam] = q;
19248                     this.store.load({
19249                         params: this.getParams(q)
19250                     });
19251                     this.expand();
19252                 }
19253             }else{
19254                 this.selectedIndex = -1;
19255                 this.onLoad();   
19256             }
19257         }
19258     },
19259
19260     // private
19261     getParams : function(q){
19262         var p = {};
19263         //p[this.queryParam] = q;
19264         if(this.pageSize){
19265             p.start = 0;
19266             p.limit = this.pageSize;
19267         }
19268         return p;
19269     },
19270
19271     /**
19272      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19273      */
19274     collapse : function(){
19275         if(!this.isExpanded()){
19276             return;
19277         }
19278         this.list.hide();
19279         Roo.get(document).un('mousedown', this.collapseIf, this);
19280         Roo.get(document).un('mousewheel', this.collapseIf, this);
19281         if (!this.editable) {
19282             Roo.get(document).un('keydown', this.listKeyPress, this);
19283         }
19284         this.fireEvent('collapse', this);
19285     },
19286
19287     // private
19288     collapseIf : function(e){
19289         if(!e.within(this.wrap) && !e.within(this.list)){
19290             this.collapse();
19291         }
19292     },
19293
19294     /**
19295      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19296      */
19297     expand : function(){
19298         if(this.isExpanded() || !this.hasFocus){
19299             return;
19300         }
19301         this.list.alignTo(this.el, this.listAlign);
19302         this.list.show();
19303         Roo.get(document).on('mousedown', this.collapseIf, this);
19304         Roo.get(document).on('mousewheel', this.collapseIf, this);
19305         if (!this.editable) {
19306             Roo.get(document).on('keydown', this.listKeyPress, this);
19307         }
19308         
19309         this.fireEvent('expand', this);
19310     },
19311
19312     // private
19313     // Implements the default empty TriggerField.onTriggerClick function
19314     onTriggerClick : function(){
19315         if(this.disabled){
19316             return;
19317         }
19318         if(this.isExpanded()){
19319             this.collapse();
19320             if (!this.blockFocus) {
19321                 this.el.focus();
19322             }
19323             
19324         }else {
19325             this.hasFocus = true;
19326             if(this.triggerAction == 'all') {
19327                 this.doQuery(this.allQuery, true);
19328             } else {
19329                 this.doQuery(this.getRawValue());
19330             }
19331             if (!this.blockFocus) {
19332                 this.el.focus();
19333             }
19334         }
19335     },
19336     listKeyPress : function(e)
19337     {
19338         //Roo.log('listkeypress');
19339         // scroll to first matching element based on key pres..
19340         if (e.isSpecialKey()) {
19341             return false;
19342         }
19343         var k = String.fromCharCode(e.getKey()).toUpperCase();
19344         //Roo.log(k);
19345         var match  = false;
19346         var csel = this.view.getSelectedNodes();
19347         var cselitem = false;
19348         if (csel.length) {
19349             var ix = this.view.indexOf(csel[0]);
19350             cselitem  = this.store.getAt(ix);
19351             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19352                 cselitem = false;
19353             }
19354             
19355         }
19356         
19357         this.store.each(function(v) { 
19358             if (cselitem) {
19359                 // start at existing selection.
19360                 if (cselitem.id == v.id) {
19361                     cselitem = false;
19362                 }
19363                 return;
19364             }
19365                 
19366             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19367                 match = this.store.indexOf(v);
19368                 return false;
19369             }
19370         }, this);
19371         
19372         if (match === false) {
19373             return true; // no more action?
19374         }
19375         // scroll to?
19376         this.view.select(match);
19377         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19378         sn.scrollIntoView(sn.dom.parentNode, false);
19379     } 
19380
19381     /** 
19382     * @cfg {Boolean} grow 
19383     * @hide 
19384     */
19385     /** 
19386     * @cfg {Number} growMin 
19387     * @hide 
19388     */
19389     /** 
19390     * @cfg {Number} growMax 
19391     * @hide 
19392     */
19393     /**
19394      * @hide
19395      * @method autoSize
19396      */
19397 });/*
19398  * Copyright(c) 2010-2012, Roo J Solutions Limited
19399  *
19400  * Licence LGPL
19401  *
19402  */
19403
19404 /**
19405  * @class Roo.form.ComboBoxArray
19406  * @extends Roo.form.TextField
19407  * A facebook style adder... for lists of email / people / countries  etc...
19408  * pick multiple items from a combo box, and shows each one.
19409  *
19410  *  Fred [x]  Brian [x]  [Pick another |v]
19411  *
19412  *
19413  *  For this to work: it needs various extra information
19414  *    - normal combo problay has
19415  *      name, hiddenName
19416  *    + displayField, valueField
19417  *
19418  *    For our purpose...
19419  *
19420  *
19421  *   If we change from 'extends' to wrapping...
19422  *   
19423  *  
19424  *
19425  
19426  
19427  * @constructor
19428  * Create a new ComboBoxArray.
19429  * @param {Object} config Configuration options
19430  */
19431  
19432
19433 Roo.form.ComboBoxArray = function(config)
19434 {
19435     this.addEvents({
19436         /**
19437          * @event beforeremove
19438          * Fires before remove the value from the list
19439              * @param {Roo.form.ComboBoxArray} _self This combo box array
19440              * @param {Roo.form.ComboBoxArray.Item} item removed item
19441              */
19442         'beforeremove' : true,
19443         /**
19444          * @event remove
19445          * Fires when remove the value from the list
19446              * @param {Roo.form.ComboBoxArray} _self This combo box array
19447              * @param {Roo.form.ComboBoxArray.Item} item removed item
19448              */
19449         'remove' : true
19450         
19451         
19452     });
19453     
19454     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19455     
19456     this.items = new Roo.util.MixedCollection(false);
19457     
19458     // construct the child combo...
19459     
19460     
19461     
19462     
19463    
19464     
19465 }
19466
19467  
19468 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19469
19470     /**
19471      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19472      */
19473     
19474     lastData : false,
19475     
19476     // behavies liek a hiddne field
19477     inputType:      'hidden',
19478     /**
19479      * @cfg {Number} width The width of the box that displays the selected element
19480      */ 
19481     width:          300,
19482
19483     
19484     
19485     /**
19486      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19487      */
19488     name : false,
19489     /**
19490      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19491      */
19492     hiddenName : false,
19493       /**
19494      * @cfg {String} seperator    The value seperator normally ',' 
19495      */
19496     seperator : ',',
19497     
19498     // private the array of items that are displayed..
19499     items  : false,
19500     // private - the hidden field el.
19501     hiddenEl : false,
19502     // private - the filed el..
19503     el : false,
19504     
19505     //validateValue : function() { return true; }, // all values are ok!
19506     //onAddClick: function() { },
19507     
19508     onRender : function(ct, position) 
19509     {
19510         
19511         // create the standard hidden element
19512         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19513         
19514         
19515         // give fake names to child combo;
19516         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19517         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19518         
19519         this.combo = Roo.factory(this.combo, Roo.form);
19520         this.combo.onRender(ct, position);
19521         if (typeof(this.combo.width) != 'undefined') {
19522             this.combo.onResize(this.combo.width,0);
19523         }
19524         
19525         this.combo.initEvents();
19526         
19527         // assigned so form know we need to do this..
19528         this.store          = this.combo.store;
19529         this.valueField     = this.combo.valueField;
19530         this.displayField   = this.combo.displayField ;
19531         
19532         
19533         this.combo.wrap.addClass('x-cbarray-grp');
19534         
19535         var cbwrap = this.combo.wrap.createChild(
19536             {tag: 'div', cls: 'x-cbarray-cb'},
19537             this.combo.el.dom
19538         );
19539         
19540              
19541         this.hiddenEl = this.combo.wrap.createChild({
19542             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19543         });
19544         this.el = this.combo.wrap.createChild({
19545             tag: 'input',  type:'hidden' , name: this.name, value : ''
19546         });
19547          //   this.el.dom.removeAttribute("name");
19548         
19549         
19550         this.outerWrap = this.combo.wrap;
19551         this.wrap = cbwrap;
19552         
19553         this.outerWrap.setWidth(this.width);
19554         this.outerWrap.dom.removeChild(this.el.dom);
19555         
19556         this.wrap.dom.appendChild(this.el.dom);
19557         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19558         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19559         
19560         this.combo.trigger.setStyle('position','relative');
19561         this.combo.trigger.setStyle('left', '0px');
19562         this.combo.trigger.setStyle('top', '2px');
19563         
19564         this.combo.el.setStyle('vertical-align', 'text-bottom');
19565         
19566         //this.trigger.setStyle('vertical-align', 'top');
19567         
19568         // this should use the code from combo really... on('add' ....)
19569         if (this.adder) {
19570             
19571         
19572             this.adder = this.outerWrap.createChild(
19573                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19574             var _t = this;
19575             this.adder.on('click', function(e) {
19576                 _t.fireEvent('adderclick', this, e);
19577             }, _t);
19578         }
19579         //var _t = this;
19580         //this.adder.on('click', this.onAddClick, _t);
19581         
19582         
19583         this.combo.on('select', function(cb, rec, ix) {
19584             this.addItem(rec.data);
19585             
19586             cb.setValue('');
19587             cb.el.dom.value = '';
19588             //cb.lastData = rec.data;
19589             // add to list
19590             
19591         }, this);
19592         
19593         
19594     },
19595     
19596     
19597     getName: function()
19598     {
19599         // returns hidden if it's set..
19600         if (!this.rendered) {return ''};
19601         return  this.hiddenName ? this.hiddenName : this.name;
19602         
19603     },
19604     
19605     
19606     onResize: function(w, h){
19607         
19608         return;
19609         // not sure if this is needed..
19610         //this.combo.onResize(w,h);
19611         
19612         if(typeof w != 'number'){
19613             // we do not handle it!?!?
19614             return;
19615         }
19616         var tw = this.combo.trigger.getWidth();
19617         tw += this.addicon ? this.addicon.getWidth() : 0;
19618         tw += this.editicon ? this.editicon.getWidth() : 0;
19619         var x = w - tw;
19620         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19621             
19622         this.combo.trigger.setStyle('left', '0px');
19623         
19624         if(this.list && this.listWidth === undefined){
19625             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19626             this.list.setWidth(lw);
19627             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19628         }
19629         
19630     
19631         
19632     },
19633     
19634     addItem: function(rec)
19635     {
19636         var valueField = this.combo.valueField;
19637         var displayField = this.combo.displayField;
19638         
19639         if (this.items.indexOfKey(rec[valueField]) > -1) {
19640             //console.log("GOT " + rec.data.id);
19641             return;
19642         }
19643         
19644         var x = new Roo.form.ComboBoxArray.Item({
19645             //id : rec[this.idField],
19646             data : rec,
19647             displayField : displayField ,
19648             tipField : displayField ,
19649             cb : this
19650         });
19651         // use the 
19652         this.items.add(rec[valueField],x);
19653         // add it before the element..
19654         this.updateHiddenEl();
19655         x.render(this.outerWrap, this.wrap.dom);
19656         // add the image handler..
19657     },
19658     
19659     updateHiddenEl : function()
19660     {
19661         this.validate();
19662         if (!this.hiddenEl) {
19663             return;
19664         }
19665         var ar = [];
19666         var idField = this.combo.valueField;
19667         
19668         this.items.each(function(f) {
19669             ar.push(f.data[idField]);
19670         });
19671         this.hiddenEl.dom.value = ar.join(this.seperator);
19672         this.validate();
19673     },
19674     
19675     reset : function()
19676     {
19677         this.items.clear();
19678         
19679         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19680            el.remove();
19681         });
19682         
19683         this.el.dom.value = '';
19684         if (this.hiddenEl) {
19685             this.hiddenEl.dom.value = '';
19686         }
19687         
19688     },
19689     getValue: function()
19690     {
19691         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19692     },
19693     setValue: function(v) // not a valid action - must use addItems..
19694     {
19695         
19696         this.reset();
19697          
19698         if (this.store.isLocal && (typeof(v) == 'string')) {
19699             // then we can use the store to find the values..
19700             // comma seperated at present.. this needs to allow JSON based encoding..
19701             this.hiddenEl.value  = v;
19702             var v_ar = [];
19703             Roo.each(v.split(this.seperator), function(k) {
19704                 Roo.log("CHECK " + this.valueField + ',' + k);
19705                 var li = this.store.query(this.valueField, k);
19706                 if (!li.length) {
19707                     return;
19708                 }
19709                 var add = {};
19710                 add[this.valueField] = k;
19711                 add[this.displayField] = li.item(0).data[this.displayField];
19712                 
19713                 this.addItem(add);
19714             }, this) 
19715              
19716         }
19717         if (typeof(v) == 'object' ) {
19718             // then let's assume it's an array of objects..
19719             Roo.each(v, function(l) {
19720                 var add = l;
19721                 if (typeof(l) == 'string') {
19722                     add = {};
19723                     add[this.valueField] = l;
19724                     add[this.displayField] = l
19725                 }
19726                 this.addItem(add);
19727             }, this);
19728              
19729         }
19730         
19731         
19732     },
19733     setFromData: function(v)
19734     {
19735         // this recieves an object, if setValues is called.
19736         this.reset();
19737         this.el.dom.value = v[this.displayField];
19738         this.hiddenEl.dom.value = v[this.valueField];
19739         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19740             return;
19741         }
19742         var kv = v[this.valueField];
19743         var dv = v[this.displayField];
19744         kv = typeof(kv) != 'string' ? '' : kv;
19745         dv = typeof(dv) != 'string' ? '' : dv;
19746         
19747         
19748         var keys = kv.split(this.seperator);
19749         var display = dv.split(this.seperator);
19750         for (var i = 0 ; i < keys.length; i++) {
19751             add = {};
19752             add[this.valueField] = keys[i];
19753             add[this.displayField] = display[i];
19754             this.addItem(add);
19755         }
19756       
19757         
19758     },
19759     
19760     /**
19761      * Validates the combox array value
19762      * @return {Boolean} True if the value is valid, else false
19763      */
19764     validate : function(){
19765         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19766             this.clearInvalid();
19767             return true;
19768         }
19769         return false;
19770     },
19771     
19772     validateValue : function(value){
19773         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19774         
19775     },
19776     
19777     /*@
19778      * overide
19779      * 
19780      */
19781     isDirty : function() {
19782         if(this.disabled) {
19783             return false;
19784         }
19785         
19786         try {
19787             var d = Roo.decode(String(this.originalValue));
19788         } catch (e) {
19789             return String(this.getValue()) !== String(this.originalValue);
19790         }
19791         
19792         var originalValue = [];
19793         
19794         for (var i = 0; i < d.length; i++){
19795             originalValue.push(d[i][this.valueField]);
19796         }
19797         
19798         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19799         
19800     }
19801     
19802 });
19803
19804
19805
19806 /**
19807  * @class Roo.form.ComboBoxArray.Item
19808  * @extends Roo.BoxComponent
19809  * A selected item in the list
19810  *  Fred [x]  Brian [x]  [Pick another |v]
19811  * 
19812  * @constructor
19813  * Create a new item.
19814  * @param {Object} config Configuration options
19815  */
19816  
19817 Roo.form.ComboBoxArray.Item = function(config) {
19818     config.id = Roo.id();
19819     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19820 }
19821
19822 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19823     data : {},
19824     cb: false,
19825     displayField : false,
19826     tipField : false,
19827     
19828     
19829     defaultAutoCreate : {
19830         tag: 'div',
19831         cls: 'x-cbarray-item',
19832         cn : [ 
19833             { tag: 'div' },
19834             {
19835                 tag: 'img',
19836                 width:16,
19837                 height : 16,
19838                 src : Roo.BLANK_IMAGE_URL ,
19839                 align: 'center'
19840             }
19841         ]
19842         
19843     },
19844     
19845  
19846     onRender : function(ct, position)
19847     {
19848         Roo.form.Field.superclass.onRender.call(this, ct, position);
19849         
19850         if(!this.el){
19851             var cfg = this.getAutoCreate();
19852             this.el = ct.createChild(cfg, position);
19853         }
19854         
19855         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19856         
19857         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19858             this.cb.renderer(this.data) :
19859             String.format('{0}',this.data[this.displayField]);
19860         
19861             
19862         this.el.child('div').dom.setAttribute('qtip',
19863                         String.format('{0}',this.data[this.tipField])
19864         );
19865         
19866         this.el.child('img').on('click', this.remove, this);
19867         
19868     },
19869    
19870     remove : function()
19871     {
19872         if(this.cb.disabled){
19873             return;
19874         }
19875         
19876         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19877             this.cb.items.remove(this);
19878             this.el.child('img').un('click', this.remove, this);
19879             this.el.remove();
19880             this.cb.updateHiddenEl();
19881
19882             this.cb.fireEvent('remove', this.cb, this);
19883         }
19884         
19885     }
19886 });/*
19887  * RooJS Library 1.1.1
19888  * Copyright(c) 2008-2011  Alan Knowles
19889  *
19890  * License - LGPL
19891  */
19892  
19893
19894 /**
19895  * @class Roo.form.ComboNested
19896  * @extends Roo.form.ComboBox
19897  * A combobox for that allows selection of nested items in a list,
19898  * eg.
19899  *
19900  *  Book
19901  *    -> red
19902  *    -> green
19903  *  Table
19904  *    -> square
19905  *      ->red
19906  *      ->green
19907  *    -> rectangle
19908  *      ->green
19909  *      
19910  * 
19911  * @constructor
19912  * Create a new ComboNested
19913  * @param {Object} config Configuration options
19914  */
19915 Roo.form.ComboNested = function(config){
19916     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19917     // should verify some data...
19918     // like
19919     // hiddenName = required..
19920     // displayField = required
19921     // valudField == required
19922     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19923     var _t = this;
19924     Roo.each(req, function(e) {
19925         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19926             throw "Roo.form.ComboNested : missing value for: " + e;
19927         }
19928     });
19929      
19930     
19931 };
19932
19933 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19934    
19935     /*
19936      * @config {Number} max Number of columns to show
19937      */
19938     
19939     maxColumns : 3,
19940    
19941     list : null, // the outermost div..
19942     innerLists : null, // the
19943     views : null,
19944     stores : null,
19945     // private
19946     loadingChildren : false,
19947     
19948     onRender : function(ct, position)
19949     {
19950         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19951         
19952         if(this.hiddenName){
19953             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19954                     'before', true);
19955             this.hiddenField.value =
19956                 this.hiddenValue !== undefined ? this.hiddenValue :
19957                 this.value !== undefined ? this.value : '';
19958
19959             // prevent input submission
19960             this.el.dom.removeAttribute('name');
19961              
19962              
19963         }
19964         
19965         if(Roo.isGecko){
19966             this.el.dom.setAttribute('autocomplete', 'off');
19967         }
19968
19969         var cls = 'x-combo-list';
19970
19971         this.list = new Roo.Layer({
19972             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19973         });
19974
19975         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19976         this.list.setWidth(lw);
19977         this.list.swallowEvent('mousewheel');
19978         this.assetHeight = 0;
19979
19980         if(this.title){
19981             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19982             this.assetHeight += this.header.getHeight();
19983         }
19984         this.innerLists = [];
19985         this.views = [];
19986         this.stores = [];
19987         for (var i =0 ; i < this.maxColumns; i++) {
19988             this.onRenderList( cls, i);
19989         }
19990         
19991         // always needs footer, as we are going to have an 'OK' button.
19992         this.footer = this.list.createChild({cls:cls+'-ft'});
19993         this.pageTb = new Roo.Toolbar(this.footer);  
19994         var _this = this;
19995         this.pageTb.add(  {
19996             
19997             text: 'Done',
19998             handler: function()
19999             {
20000                 _this.collapse();
20001             }
20002         });
20003         
20004         if ( this.allowBlank && !this.disableClear) {
20005             
20006             this.pageTb.add(new Roo.Toolbar.Fill(), {
20007                 cls: 'x-btn-icon x-btn-clear',
20008                 text: '&#160;',
20009                 handler: function()
20010                 {
20011                     _this.collapse();
20012                     _this.clearValue();
20013                     _this.onSelect(false, -1);
20014                 }
20015             });
20016         }
20017         if (this.footer) {
20018             this.assetHeight += this.footer.getHeight();
20019         }
20020         
20021     },
20022     onRenderList : function (  cls, i)
20023     {
20024         
20025         var lw = Math.floor(
20026                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20027         );
20028         
20029         this.list.setWidth(lw); // default to '1'
20030
20031         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20032         //il.on('mouseover', this.onViewOver, this, { list:  i });
20033         //il.on('mousemove', this.onViewMove, this, { list:  i });
20034         il.setWidth(lw);
20035         il.setStyle({ 'overflow-x' : 'hidden'});
20036
20037         if(!this.tpl){
20038             this.tpl = new Roo.Template({
20039                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20040                 isEmpty: function (value, allValues) {
20041                     //Roo.log(value);
20042                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20043                     return dl ? 'has-children' : 'no-children'
20044                 }
20045             });
20046         }
20047         
20048         var store  = this.store;
20049         if (i > 0) {
20050             store  = new Roo.data.SimpleStore({
20051                 //fields : this.store.reader.meta.fields,
20052                 reader : this.store.reader,
20053                 data : [ ]
20054             });
20055         }
20056         this.stores[i]  = store;
20057                   
20058         var view = this.views[i] = new Roo.View(
20059             il,
20060             this.tpl,
20061             {
20062                 singleSelect:true,
20063                 store: store,
20064                 selectedClass: this.selectedClass
20065             }
20066         );
20067         view.getEl().setWidth(lw);
20068         view.getEl().setStyle({
20069             position: i < 1 ? 'relative' : 'absolute',
20070             top: 0,
20071             left: (i * lw ) + 'px',
20072             display : i > 0 ? 'none' : 'block'
20073         });
20074         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20075         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20076         //view.on('click', this.onViewClick, this, { list : i });
20077
20078         store.on('beforeload', this.onBeforeLoad, this);
20079         store.on('load',  this.onLoad, this, { list  : i});
20080         store.on('loadexception', this.onLoadException, this);
20081
20082         // hide the other vies..
20083         
20084         
20085         
20086     },
20087       
20088     restrictHeight : function()
20089     {
20090         var mh = 0;
20091         Roo.each(this.innerLists, function(il,i) {
20092             var el = this.views[i].getEl();
20093             el.dom.style.height = '';
20094             var inner = el.dom;
20095             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20096             // only adjust heights on other ones..
20097             mh = Math.max(h, mh);
20098             if (i < 1) {
20099                 
20100                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20101                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20102                
20103             }
20104             
20105             
20106         }, this);
20107         
20108         this.list.beginUpdate();
20109         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20110         this.list.alignTo(this.el, this.listAlign);
20111         this.list.endUpdate();
20112         
20113     },
20114      
20115     
20116     // -- store handlers..
20117     // private
20118     onBeforeLoad : function()
20119     {
20120         if(!this.hasFocus){
20121             return;
20122         }
20123         this.innerLists[0].update(this.loadingText ?
20124                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20125         this.restrictHeight();
20126         this.selectedIndex = -1;
20127     },
20128     // private
20129     onLoad : function(a,b,c,d)
20130     {
20131         if (!this.loadingChildren) {
20132             // then we are loading the top level. - hide the children
20133             for (var i = 1;i < this.views.length; i++) {
20134                 this.views[i].getEl().setStyle({ display : 'none' });
20135             }
20136             var lw = Math.floor(
20137                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20138             );
20139         
20140              this.list.setWidth(lw); // default to '1'
20141
20142             
20143         }
20144         if(!this.hasFocus){
20145             return;
20146         }
20147         
20148         if(this.store.getCount() > 0) {
20149             this.expand();
20150             this.restrictHeight();   
20151         } else {
20152             this.onEmptyResults();
20153         }
20154         
20155         if (!this.loadingChildren) {
20156             this.selectActive();
20157         }
20158         /*
20159         this.stores[1].loadData([]);
20160         this.stores[2].loadData([]);
20161         this.views
20162         */    
20163     
20164         //this.el.focus();
20165     },
20166     
20167     
20168     // private
20169     onLoadException : function()
20170     {
20171         this.collapse();
20172         Roo.log(this.store.reader.jsonData);
20173         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20174             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20175         }
20176         
20177         
20178     },
20179     // no cleaning of leading spaces on blur here.
20180     cleanLeadingSpace : function(e) { },
20181     
20182
20183     onSelectChange : function (view, sels, opts )
20184     {
20185         var ix = view.getSelectedIndexes();
20186          
20187         if (opts.list > this.maxColumns - 2) {
20188             if (view.store.getCount()<  1) {
20189                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20190
20191             } else  {
20192                 if (ix.length) {
20193                     // used to clear ?? but if we are loading unselected 
20194                     this.setFromData(view.store.getAt(ix[0]).data);
20195                 }
20196                 
20197             }
20198             
20199             return;
20200         }
20201         
20202         if (!ix.length) {
20203             // this get's fired when trigger opens..
20204            // this.setFromData({});
20205             var str = this.stores[opts.list+1];
20206             str.data.clear(); // removeall wihtout the fire events..
20207             return;
20208         }
20209         
20210         var rec = view.store.getAt(ix[0]);
20211          
20212         this.setFromData(rec.data);
20213         this.fireEvent('select', this, rec, ix[0]);
20214         
20215         var lw = Math.floor(
20216              (
20217                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20218              ) / this.maxColumns
20219         );
20220         this.loadingChildren = true;
20221         this.stores[opts.list+1].loadDataFromChildren( rec );
20222         this.loadingChildren = false;
20223         var dl = this.stores[opts.list+1]. getTotalCount();
20224         
20225         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20226         
20227         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20228         for (var i = opts.list+2; i < this.views.length;i++) {
20229             this.views[i].getEl().setStyle({ display : 'none' });
20230         }
20231         
20232         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20233         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20234         
20235         if (this.isLoading) {
20236            // this.selectActive(opts.list);
20237         }
20238          
20239     },
20240     
20241     
20242     
20243     
20244     onDoubleClick : function()
20245     {
20246         this.collapse(); //??
20247     },
20248     
20249      
20250     
20251     
20252     
20253     // private
20254     recordToStack : function(store, prop, value, stack)
20255     {
20256         var cstore = new Roo.data.SimpleStore({
20257             //fields : this.store.reader.meta.fields, // we need array reader.. for
20258             reader : this.store.reader,
20259             data : [ ]
20260         });
20261         var _this = this;
20262         var record  = false;
20263         var srec = false;
20264         if(store.getCount() < 1){
20265             return false;
20266         }
20267         store.each(function(r){
20268             if(r.data[prop] == value){
20269                 record = r;
20270             srec = r;
20271                 return false;
20272             }
20273             if (r.data.cn && r.data.cn.length) {
20274                 cstore.loadDataFromChildren( r);
20275                 var cret = _this.recordToStack(cstore, prop, value, stack);
20276                 if (cret !== false) {
20277                     record = cret;
20278                     srec = r;
20279                     return false;
20280                 }
20281             }
20282              
20283             return true;
20284         });
20285         if (record == false) {
20286             return false
20287         }
20288         stack.unshift(srec);
20289         return record;
20290     },
20291     
20292     /*
20293      * find the stack of stores that match our value.
20294      *
20295      * 
20296      */
20297     
20298     selectActive : function ()
20299     {
20300         // if store is not loaded, then we will need to wait for that to happen first.
20301         var stack = [];
20302         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20303         for (var i = 0; i < stack.length; i++ ) {
20304             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20305         }
20306         
20307     }
20308         
20309          
20310     
20311     
20312     
20313     
20314 });/*
20315  * Based on:
20316  * Ext JS Library 1.1.1
20317  * Copyright(c) 2006-2007, Ext JS, LLC.
20318  *
20319  * Originally Released Under LGPL - original licence link has changed is not relivant.
20320  *
20321  * Fork - LGPL
20322  * <script type="text/javascript">
20323  */
20324 /**
20325  * @class Roo.form.Checkbox
20326  * @extends Roo.form.Field
20327  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20328  * @constructor
20329  * Creates a new Checkbox
20330  * @param {Object} config Configuration options
20331  */
20332 Roo.form.Checkbox = function(config){
20333     Roo.form.Checkbox.superclass.constructor.call(this, config);
20334     this.addEvents({
20335         /**
20336          * @event check
20337          * Fires when the checkbox is checked or unchecked.
20338              * @param {Roo.form.Checkbox} this This checkbox
20339              * @param {Boolean} checked The new checked value
20340              */
20341         check : true
20342     });
20343 };
20344
20345 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20346     /**
20347      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20348      */
20349     focusClass : undefined,
20350     /**
20351      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20352      */
20353     fieldClass: "x-form-field",
20354     /**
20355      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20356      */
20357     checked: false,
20358     /**
20359      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20360      * {tag: "input", type: "checkbox", autocomplete: "off"})
20361      */
20362     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20363     /**
20364      * @cfg {String} boxLabel The text that appears beside the checkbox
20365      */
20366     boxLabel : "",
20367     /**
20368      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20369      */  
20370     inputValue : '1',
20371     /**
20372      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20373      */
20374      valueOff: '0', // value when not checked..
20375
20376     actionMode : 'viewEl', 
20377     //
20378     // private
20379     itemCls : 'x-menu-check-item x-form-item',
20380     groupClass : 'x-menu-group-item',
20381     inputType : 'hidden',
20382     
20383     
20384     inSetChecked: false, // check that we are not calling self...
20385     
20386     inputElement: false, // real input element?
20387     basedOn: false, // ????
20388     
20389     isFormField: true, // not sure where this is needed!!!!
20390
20391     onResize : function(){
20392         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20393         if(!this.boxLabel){
20394             this.el.alignTo(this.wrap, 'c-c');
20395         }
20396     },
20397
20398     initEvents : function(){
20399         Roo.form.Checkbox.superclass.initEvents.call(this);
20400         this.el.on("click", this.onClick,  this);
20401         this.el.on("change", this.onClick,  this);
20402     },
20403
20404
20405     getResizeEl : function(){
20406         return this.wrap;
20407     },
20408
20409     getPositionEl : function(){
20410         return this.wrap;
20411     },
20412
20413     // private
20414     onRender : function(ct, position){
20415         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20416         /*
20417         if(this.inputValue !== undefined){
20418             this.el.dom.value = this.inputValue;
20419         }
20420         */
20421         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20422         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20423         var viewEl = this.wrap.createChild({ 
20424             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20425         this.viewEl = viewEl;   
20426         this.wrap.on('click', this.onClick,  this); 
20427         
20428         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20429         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20430         
20431         
20432         
20433         if(this.boxLabel){
20434             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20435         //    viewEl.on('click', this.onClick,  this); 
20436         }
20437         //if(this.checked){
20438             this.setChecked(this.checked);
20439         //}else{
20440             //this.checked = this.el.dom;
20441         //}
20442
20443     },
20444
20445     // private
20446     initValue : Roo.emptyFn,
20447
20448     /**
20449      * Returns the checked state of the checkbox.
20450      * @return {Boolean} True if checked, else false
20451      */
20452     getValue : function(){
20453         if(this.el){
20454             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20455         }
20456         return this.valueOff;
20457         
20458     },
20459
20460         // private
20461     onClick : function(){ 
20462         if (this.disabled) {
20463             return;
20464         }
20465         this.setChecked(!this.checked);
20466
20467         //if(this.el.dom.checked != this.checked){
20468         //    this.setValue(this.el.dom.checked);
20469        // }
20470     },
20471
20472     /**
20473      * Sets the checked state of the checkbox.
20474      * On is always based on a string comparison between inputValue and the param.
20475      * @param {Boolean/String} value - the value to set 
20476      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20477      */
20478     setValue : function(v,suppressEvent){
20479         
20480         
20481         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20482         //if(this.el && this.el.dom){
20483         //    this.el.dom.checked = this.checked;
20484         //    this.el.dom.defaultChecked = this.checked;
20485         //}
20486         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20487         //this.fireEvent("check", this, this.checked);
20488     },
20489     // private..
20490     setChecked : function(state,suppressEvent)
20491     {
20492         if (this.inSetChecked) {
20493             this.checked = state;
20494             return;
20495         }
20496         
20497     
20498         if(this.wrap){
20499             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20500         }
20501         this.checked = state;
20502         if(suppressEvent !== true){
20503             this.fireEvent('check', this, state);
20504         }
20505         this.inSetChecked = true;
20506         this.el.dom.value = state ? this.inputValue : this.valueOff;
20507         this.inSetChecked = false;
20508         
20509     },
20510     // handle setting of hidden value by some other method!!?!?
20511     setFromHidden: function()
20512     {
20513         if(!this.el){
20514             return;
20515         }
20516         //console.log("SET FROM HIDDEN");
20517         //alert('setFrom hidden');
20518         this.setValue(this.el.dom.value);
20519     },
20520     
20521     onDestroy : function()
20522     {
20523         if(this.viewEl){
20524             Roo.get(this.viewEl).remove();
20525         }
20526          
20527         Roo.form.Checkbox.superclass.onDestroy.call(this);
20528     },
20529     
20530     setBoxLabel : function(str)
20531     {
20532         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20533     }
20534
20535 });/*
20536  * Based on:
20537  * Ext JS Library 1.1.1
20538  * Copyright(c) 2006-2007, Ext JS, LLC.
20539  *
20540  * Originally Released Under LGPL - original licence link has changed is not relivant.
20541  *
20542  * Fork - LGPL
20543  * <script type="text/javascript">
20544  */
20545  
20546 /**
20547  * @class Roo.form.Radio
20548  * @extends Roo.form.Checkbox
20549  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20550  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20551  * @constructor
20552  * Creates a new Radio
20553  * @param {Object} config Configuration options
20554  */
20555 Roo.form.Radio = function(){
20556     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20557 };
20558 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20559     inputType: 'radio',
20560
20561     /**
20562      * If this radio is part of a group, it will return the selected value
20563      * @return {String}
20564      */
20565     getGroupValue : function(){
20566         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20567     },
20568     
20569     
20570     onRender : function(ct, position){
20571         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20572         
20573         if(this.inputValue !== undefined){
20574             this.el.dom.value = this.inputValue;
20575         }
20576          
20577         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20578         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20579         //var viewEl = this.wrap.createChild({ 
20580         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20581         //this.viewEl = viewEl;   
20582         //this.wrap.on('click', this.onClick,  this); 
20583         
20584         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20585         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20586         
20587         
20588         
20589         if(this.boxLabel){
20590             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20591         //    viewEl.on('click', this.onClick,  this); 
20592         }
20593          if(this.checked){
20594             this.el.dom.checked =   'checked' ;
20595         }
20596          
20597     } 
20598     
20599     
20600 });Roo.rtf = {}; // namespace
20601 Roo.rtf.Hex = function(hex)
20602 {
20603     this.hexstr = hex;
20604 };
20605 Roo.rtf.Paragraph = function(opts)
20606 {
20607     this.content = []; ///??? is that used?
20608 };Roo.rtf.Span = function(opts)
20609 {
20610     this.value = opts.value;
20611 };
20612
20613 Roo.rtf.Group = function(parent)
20614 {
20615     // we dont want to acutally store parent - it will make debug a nightmare..
20616     this.content = [];
20617     this.cn  = [];
20618      
20619        
20620     
20621 };
20622
20623 Roo.rtf.Group.prototype = {
20624     ignorable : false,
20625     content: false,
20626     cn: false,
20627     addContent : function(node) {
20628         // could set styles...
20629         this.content.push(node);
20630     },
20631     addChild : function(cn)
20632     {
20633         this.cn.push(cn);
20634     },
20635     // only for images really...
20636     toDataURL : function()
20637     {
20638         var mimetype = false;
20639         switch(true) {
20640             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20641                 mimetype = "image/png";
20642                 break;
20643              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20644                 mimetype = "image/jpeg";
20645                 break;
20646             default :
20647                 return 'about:blank'; // ?? error?
20648         }
20649         
20650         
20651         var hexstring = this.content[this.content.length-1].value;
20652         
20653         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20654             return String.fromCharCode(parseInt(a, 16));
20655         }).join(""));
20656     }
20657     
20658 };
20659 // this looks like it's normally the {rtf{ .... }}
20660 Roo.rtf.Document = function()
20661 {
20662     // we dont want to acutally store parent - it will make debug a nightmare..
20663     this.rtlch  = [];
20664     this.content = [];
20665     this.cn = [];
20666     
20667 };
20668 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20669     addChild : function(cn)
20670     {
20671         this.cn.push(cn);
20672         switch(cn.type) {
20673             case 'rtlch': // most content seems to be inside this??
20674             case 'listtext':
20675             case 'shpinst':
20676                 this.rtlch.push(cn);
20677                 return;
20678             default:
20679                 this[cn.type] = cn;
20680         }
20681         
20682     },
20683     
20684     getElementsByType : function(type)
20685     {
20686         var ret =  [];
20687         this._getElementsByType(type, ret, this.cn, 'rtf');
20688         return ret;
20689     },
20690     _getElementsByType : function (type, ret, search_array, path)
20691     {
20692         search_array.forEach(function(n,i) {
20693             if (n.type == type) {
20694                 n.path = path + '/' + n.type + ':' + i;
20695                 ret.push(n);
20696             }
20697             if (n.cn.length > 0) {
20698                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20699             }
20700         },this);
20701     }
20702     
20703 });
20704  
20705 Roo.rtf.Ctrl = function(opts)
20706 {
20707     this.value = opts.value;
20708     this.param = opts.param;
20709 };
20710 /**
20711  *
20712  *
20713  * based on this https://github.com/iarna/rtf-parser
20714  * it's really only designed to extract pict from pasted RTF 
20715  *
20716  * usage:
20717  *
20718  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20719  *  
20720  *
20721  */
20722
20723  
20724
20725
20726
20727 Roo.rtf.Parser = function(text) {
20728     //super({objectMode: true})
20729     this.text = '';
20730     this.parserState = this.parseText;
20731     
20732     // these are for interpeter...
20733     this.doc = {};
20734     ///this.parserState = this.parseTop
20735     this.groupStack = [];
20736     this.hexStore = [];
20737     this.doc = false;
20738     
20739     this.groups = []; // where we put the return.
20740     
20741     for (var ii = 0; ii < text.length; ++ii) {
20742         ++this.cpos;
20743         
20744         if (text[ii] === '\n') {
20745             ++this.row;
20746             this.col = 1;
20747         } else {
20748             ++this.col;
20749         }
20750         this.parserState(text[ii]);
20751     }
20752     
20753     
20754     
20755 };
20756 Roo.rtf.Parser.prototype = {
20757     text : '', // string being parsed..
20758     controlWord : '',
20759     controlWordParam :  '',
20760     hexChar : '',
20761     doc : false,
20762     group: false,
20763     groupStack : false,
20764     hexStore : false,
20765     
20766     
20767     cpos : 0, 
20768     row : 1, // reportin?
20769     col : 1, //
20770
20771      
20772     push : function (el)
20773     {
20774         var m = 'cmd'+ el.type;
20775         if (typeof(this[m]) == 'undefined') {
20776             Roo.log('invalid cmd:' + el.type);
20777             return;
20778         }
20779         this[m](el);
20780         //Roo.log(el);
20781     },
20782     flushHexStore : function()
20783     {
20784         if (this.hexStore.length < 1) {
20785             return;
20786         }
20787         var hexstr = this.hexStore.map(
20788             function(cmd) {
20789                 return cmd.value;
20790         }).join('');
20791         
20792         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20793               
20794             
20795         this.hexStore.splice(0)
20796         
20797     },
20798     
20799     cmdgroupstart : function()
20800     {
20801         this.flushHexStore();
20802         if (this.group) {
20803             this.groupStack.push(this.group);
20804         }
20805          // parent..
20806         if (this.doc === false) {
20807             this.group = this.doc = new Roo.rtf.Document();
20808             return;
20809             
20810         }
20811         this.group = new Roo.rtf.Group(this.group);
20812     },
20813     cmdignorable : function()
20814     {
20815         this.flushHexStore();
20816         this.group.ignorable = true;
20817     },
20818     cmdendparagraph : function()
20819     {
20820         this.flushHexStore();
20821         this.group.addContent(new Roo.rtf.Paragraph());
20822     },
20823     cmdgroupend : function ()
20824     {
20825         this.flushHexStore();
20826         var endingGroup = this.group;
20827         
20828         
20829         this.group = this.groupStack.pop();
20830         if (this.group) {
20831             this.group.addChild(endingGroup);
20832         }
20833         
20834         
20835         
20836         var doc = this.group || this.doc;
20837         //if (endingGroup instanceof FontTable) {
20838         //  doc.fonts = endingGroup.table
20839         //} else if (endingGroup instanceof ColorTable) {
20840         //  doc.colors = endingGroup.table
20841         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20842         if (endingGroup.ignorable === false) {
20843             //code
20844             this.groups.push(endingGroup);
20845            // Roo.log( endingGroup );
20846         }
20847             //Roo.each(endingGroup.content, function(item)) {
20848             //    doc.addContent(item);
20849             //}
20850             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20851         //}
20852     },
20853     cmdtext : function (cmd)
20854     {
20855         this.flushHexStore();
20856         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20857             //this.group = this.doc
20858             return;  // we really don't care about stray text...
20859         }
20860         this.group.addContent(new Roo.rtf.Span(cmd));
20861     },
20862     cmdcontrolword : function (cmd)
20863     {
20864         this.flushHexStore();
20865         if (!this.group.type) {
20866             this.group.type = cmd.value;
20867             return;
20868         }
20869         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20870         // we actually don't care about ctrl words...
20871         return ;
20872         /*
20873         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20874         if (this[method]) {
20875             this[method](cmd.param)
20876         } else {
20877             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20878         }
20879         */
20880     },
20881     cmdhexchar : function(cmd) {
20882         this.hexStore.push(cmd);
20883     },
20884     cmderror : function(cmd) {
20885         throw new Exception (cmd.value);
20886     },
20887     
20888     /*
20889       _flush (done) {
20890         if (this.text !== '\u0000') this.emitText()
20891         done()
20892       }
20893       */
20894       
20895       
20896     parseText : function(c)
20897     {
20898         if (c === '\\') {
20899             this.parserState = this.parseEscapes;
20900         } else if (c === '{') {
20901             this.emitStartGroup();
20902         } else if (c === '}') {
20903             this.emitEndGroup();
20904         } else if (c === '\x0A' || c === '\x0D') {
20905             // cr/lf are noise chars
20906         } else {
20907             this.text += c;
20908         }
20909     },
20910     
20911     parseEscapes: function (c)
20912     {
20913         if (c === '\\' || c === '{' || c === '}') {
20914             this.text += c;
20915             this.parserState = this.parseText;
20916         } else {
20917             this.parserState = this.parseControlSymbol;
20918             this.parseControlSymbol(c);
20919         }
20920     },
20921     parseControlSymbol: function(c)
20922     {
20923         if (c === '~') {
20924             this.text += '\u00a0'; // nbsp
20925             this.parserState = this.parseText
20926         } else if (c === '-') {
20927              this.text += '\u00ad'; // soft hyphen
20928         } else if (c === '_') {
20929             this.text += '\u2011'; // non-breaking hyphen
20930         } else if (c === '*') {
20931             this.emitIgnorable();
20932             this.parserState = this.parseText;
20933         } else if (c === "'") {
20934             this.parserState = this.parseHexChar;
20935         } else if (c === '|') { // formula cacter
20936             this.emitFormula();
20937             this.parserState = this.parseText;
20938         } else if (c === ':') { // subentry in an index entry
20939             this.emitIndexSubEntry();
20940             this.parserState = this.parseText;
20941         } else if (c === '\x0a') {
20942             this.emitEndParagraph();
20943             this.parserState = this.parseText;
20944         } else if (c === '\x0d') {
20945             this.emitEndParagraph();
20946             this.parserState = this.parseText;
20947         } else {
20948             this.parserState = this.parseControlWord;
20949             this.parseControlWord(c);
20950         }
20951     },
20952     parseHexChar: function (c)
20953     {
20954         if (/^[A-Fa-f0-9]$/.test(c)) {
20955             this.hexChar += c;
20956             if (this.hexChar.length >= 2) {
20957               this.emitHexChar();
20958               this.parserState = this.parseText;
20959             }
20960             return;
20961         }
20962         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20963         this.parserState = this.parseText;
20964         
20965     },
20966     parseControlWord : function(c)
20967     {
20968         if (c === ' ') {
20969             this.emitControlWord();
20970             this.parserState = this.parseText;
20971         } else if (/^[-\d]$/.test(c)) {
20972             this.parserState = this.parseControlWordParam;
20973             this.controlWordParam += c;
20974         } else if (/^[A-Za-z]$/.test(c)) {
20975           this.controlWord += c;
20976         } else {
20977           this.emitControlWord();
20978           this.parserState = this.parseText;
20979           this.parseText(c);
20980         }
20981     },
20982     parseControlWordParam : function (c) {
20983         if (/^\d$/.test(c)) {
20984           this.controlWordParam += c;
20985         } else if (c === ' ') {
20986           this.emitControlWord();
20987           this.parserState = this.parseText;
20988         } else {
20989           this.emitControlWord();
20990           this.parserState = this.parseText;
20991           this.parseText(c);
20992         }
20993     },
20994     
20995     
20996     
20997     
20998     emitText : function () {
20999         if (this.text === '') {
21000             return;
21001         }
21002         this.push({
21003             type: 'text',
21004             value: this.text,
21005             pos: this.cpos,
21006             row: this.row,
21007             col: this.col
21008         });
21009         this.text = ''
21010     },
21011     emitControlWord : function ()
21012     {
21013         this.emitText();
21014         if (this.controlWord === '') {
21015             this.emitError('empty control word');
21016         } else {
21017             this.push({
21018                   type: 'controlword',
21019                   value: this.controlWord,
21020                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21021                   pos: this.cpos,
21022                   row: this.row,
21023                   col: this.col
21024             });
21025         }
21026         this.controlWord = '';
21027         this.controlWordParam = '';
21028     },
21029     emitStartGroup : function ()
21030     {
21031         this.emitText();
21032         this.push({
21033             type: 'groupstart',
21034             pos: this.cpos,
21035             row: this.row,
21036             col: this.col
21037         });
21038     },
21039     emitEndGroup : function ()
21040     {
21041         this.emitText();
21042         this.push({
21043             type: 'groupend',
21044             pos: this.cpos,
21045             row: this.row,
21046             col: this.col
21047         });
21048     },
21049     emitIgnorable : function ()
21050     {
21051         this.emitText();
21052         this.push({
21053             type: 'ignorable',
21054             pos: this.cpos,
21055             row: this.row,
21056             col: this.col
21057         });
21058     },
21059     emitHexChar : function ()
21060     {
21061         this.emitText();
21062         this.push({
21063             type: 'hexchar',
21064             value: this.hexChar,
21065             pos: this.cpos,
21066             row: this.row,
21067             col: this.col
21068         });
21069         this.hexChar = ''
21070     },
21071     emitError : function (message)
21072     {
21073       this.emitText();
21074       this.push({
21075             type: 'error',
21076             value: message,
21077             row: this.row,
21078             col: this.col,
21079             char: this.cpos //,
21080             //stack: new Error().stack
21081         });
21082     },
21083     emitEndParagraph : function () {
21084         this.emitText();
21085         this.push({
21086             type: 'endparagraph',
21087             pos: this.cpos,
21088             row: this.row,
21089             col: this.col
21090         });
21091     }
21092      
21093 } ;
21094 Roo.htmleditor = {};
21095  
21096 /**
21097  * @class Roo.htmleditor.Filter
21098  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21099  * @cfg {DomElement} node The node to iterate and filter
21100  * @cfg {boolean|String|Array} tag Tags to replace 
21101  * @constructor
21102  * Create a new Filter.
21103  * @param {Object} config Configuration options
21104  */
21105
21106
21107
21108 Roo.htmleditor.Filter = function(cfg) {
21109     Roo.apply(this.cfg);
21110     // this does not actually call walk as it's really just a abstract class
21111 }
21112
21113
21114 Roo.htmleditor.Filter.prototype = {
21115     
21116     node: false,
21117     
21118     tag: false,
21119
21120     // overrride to do replace comments.
21121     replaceComment : false,
21122     
21123     // overrride to do replace or do stuff with tags..
21124     replaceTag : false,
21125     
21126     walk : function(dom)
21127     {
21128         Roo.each( Array.from(dom.childNodes), function( e ) {
21129             switch(true) {
21130                 
21131                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
21132                     this.replaceComment(e);
21133                     return;
21134                 
21135                 case e.nodeType != 1: //not a node.
21136                     return;
21137                 
21138                 case this.tag === true: // everything
21139                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21140                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21141                     if (this.replaceTag && false === this.replaceTag(e)) {
21142                         return;
21143                     }
21144                     if (e.hasChildNodes()) {
21145                         this.walk(e);
21146                     }
21147                     return;
21148                 
21149                 default:    // tags .. that do not match.
21150                     if (e.hasChildNodes()) {
21151                         this.walk(e);
21152                     }
21153             }
21154             
21155         }, this);
21156         
21157     }
21158 }; 
21159
21160 /**
21161  * @class Roo.htmleditor.FilterAttributes
21162  * clean attributes and  styles including http:// etc.. in attribute
21163  * @constructor
21164 * Run a new Attribute Filter
21165 * @param {Object} config Configuration options
21166  */
21167 Roo.htmleditor.FilterAttributes = function(cfg)
21168 {
21169     Roo.apply(this, cfg);
21170     this.attrib_black = this.attrib_black || [];
21171     this.attrib_white = this.attrib_white || [];
21172
21173     this.attrib_clean = this.attrib_clean || [];
21174     this.style_white = this.style_white || [];
21175     this.style_black = this.style_black || [];
21176     this.walk(cfg.node);
21177 }
21178
21179 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21180 {
21181     tag: true, // all tags
21182     
21183     attrib_black : false, // array
21184     attrib_clean : false,
21185     attrib_white : false,
21186
21187     style_white : false,
21188     style_black : false,
21189      
21190      
21191     replaceTag : function(node)
21192     {
21193         if (!node.attributes || !node.attributes.length) {
21194             return true;
21195         }
21196         
21197         for (var i = node.attributes.length-1; i > -1 ; i--) {
21198             var a = node.attributes[i];
21199             //console.log(a);
21200             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21201                 node.removeAttribute(a.name);
21202                 continue;
21203             }
21204             
21205             
21206             
21207             if (a.name.toLowerCase().substr(0,2)=='on')  {
21208                 node.removeAttribute(a.name);
21209                 continue;
21210             }
21211             
21212             
21213             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21214                 node.removeAttribute(a.name);
21215                 continue;
21216             }
21217             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21218                 this.cleanAttr(node,a.name,a.value); // fixme..
21219                 continue;
21220             }
21221             if (a.name == 'style') {
21222                 this.cleanStyle(node,a.name,a.value);
21223                 continue;
21224             }
21225             /// clean up MS crap..
21226             // tecnically this should be a list of valid class'es..
21227             
21228             
21229             if (a.name == 'class') {
21230                 if (a.value.match(/^Mso/)) {
21231                     node.removeAttribute('class');
21232                 }
21233                 
21234                 if (a.value.match(/^body$/)) {
21235                     node.removeAttribute('class');
21236                 }
21237                 continue;
21238             }
21239             
21240             
21241             // style cleanup!?
21242             // class cleanup?
21243             
21244         }
21245         return true; // clean children
21246     },
21247         
21248     cleanAttr: function(node, n,v)
21249     {
21250         
21251         if (v.match(/^\./) || v.match(/^\//)) {
21252             return;
21253         }
21254         if (v.match(/^(http|https):\/\//)
21255             || v.match(/^mailto:/) 
21256             || v.match(/^ftp:/)
21257             || v.match(/^data:/)
21258             ) {
21259             return;
21260         }
21261         if (v.match(/^#/)) {
21262             return;
21263         }
21264         if (v.match(/^\{/)) { // allow template editing.
21265             return;
21266         }
21267 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21268         node.removeAttribute(n);
21269         
21270     },
21271     cleanStyle : function(node,  n,v)
21272     {
21273         if (v.match(/expression/)) { //XSS?? should we even bother..
21274             node.removeAttribute(n);
21275             return;
21276         }
21277         
21278         var parts = v.split(/;/);
21279         var clean = [];
21280         
21281         Roo.each(parts, function(p) {
21282             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21283             if (!p.length) {
21284                 return true;
21285             }
21286             var l = p.split(':').shift().replace(/\s+/g,'');
21287             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21288             
21289             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21290                 return true;
21291             }
21292             //Roo.log()
21293             // only allow 'c whitelisted system attributes'
21294             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21295                 return true;
21296             }
21297             
21298             
21299             clean.push(p);
21300             return true;
21301         },this);
21302         if (clean.length) { 
21303             node.setAttribute(n, clean.join(';'));
21304         } else {
21305             node.removeAttribute(n);
21306         }
21307         
21308     }
21309         
21310         
21311         
21312     
21313 });/**
21314  * @class Roo.htmleditor.FilterBlack
21315  * remove blacklisted elements.
21316  * @constructor
21317  * Run a new Blacklisted Filter
21318  * @param {Object} config Configuration options
21319  */
21320
21321 Roo.htmleditor.FilterBlack = function(cfg)
21322 {
21323     Roo.apply(this, cfg);
21324     this.walk(cfg.node);
21325 }
21326
21327 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21328 {
21329     tag : true, // all elements.
21330    
21331     replaceTag : function(n)
21332     {
21333         n.parentNode.removeChild(n);
21334     }
21335 });
21336 /**
21337  * @class Roo.htmleditor.FilterComment
21338  * remove comments.
21339  * @constructor
21340 * Run a new Comments Filter
21341 * @param {Object} config Configuration options
21342  */
21343 Roo.htmleditor.FilterComment = function(cfg)
21344 {
21345     this.walk(cfg.node);
21346 }
21347
21348 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21349 {
21350   
21351     replaceComment : function(n)
21352     {
21353         n.parentNode.removeChild(n);
21354     }
21355 });/**
21356  * @class Roo.htmleditor.FilterKeepChildren
21357  * remove tags but keep children
21358  * @constructor
21359  * Run a new Keep Children Filter
21360  * @param {Object} config Configuration options
21361  */
21362
21363 Roo.htmleditor.FilterKeepChildren = function(cfg)
21364 {
21365     Roo.apply(this, cfg);
21366     if (this.tag === false) {
21367         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21368     }
21369     this.walk(cfg.node);
21370 }
21371
21372 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21373 {
21374     
21375   
21376     replaceTag : function(node)
21377     {
21378         // walk children...
21379         //Roo.log(node);
21380         var ar = Array.from(node.childNodes);
21381         //remove first..
21382         for (var i = 0; i < ar.length; i++) {
21383             if (ar[i].nodeType == 1) {
21384                 if (
21385                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21386                     || // array and it matches
21387                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21388                 ) {
21389                     this.replaceTag(ar[i]); // child is blacklisted as well...
21390                     continue;
21391                 }
21392             }
21393         }  
21394         ar = Array.from(node.childNodes);
21395         for (var i = 0; i < ar.length; i++) {
21396          
21397             node.removeChild(ar[i]);
21398             // what if we need to walk these???
21399             node.parentNode.insertBefore(ar[i], node);
21400             if (this.tag !== false) {
21401                 this.walk(ar[i]);
21402                 
21403             }
21404         }
21405         node.parentNode.removeChild(node);
21406         return false; // don't walk children
21407         
21408         
21409     }
21410 });/**
21411  * @class Roo.htmleditor.FilterParagraph
21412  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21413  * like on 'push' to remove the <p> tags and replace them with line breaks.
21414  * @constructor
21415  * Run a new Paragraph Filter
21416  * @param {Object} config Configuration options
21417  */
21418
21419 Roo.htmleditor.FilterParagraph = function(cfg)
21420 {
21421     // no need to apply config.
21422     this.walk(cfg.node);
21423 }
21424
21425 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21426 {
21427     
21428      
21429     tag : 'P',
21430     
21431      
21432     replaceTag : function(node)
21433     {
21434         
21435         if (node.childNodes.length == 1 &&
21436             node.childNodes[0].nodeType == 3 &&
21437             node.childNodes[0].textContent.trim().length < 1
21438             ) {
21439             // remove and replace with '<BR>';
21440             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21441             return false; // no need to walk..
21442         }
21443         var ar = Array.from(node.childNodes);
21444         for (var i = 0; i < ar.length; i++) {
21445             node.removeChild(ar[i]);
21446             // what if we need to walk these???
21447             node.parentNode.insertBefore(ar[i], node);
21448         }
21449         // now what about this?
21450         // <p> &nbsp; </p>
21451         
21452         // double BR.
21453         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21454         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21455         node.parentNode.removeChild(node);
21456         
21457         return false;
21458
21459     }
21460     
21461 });/**
21462  * @class Roo.htmleditor.FilterSpan
21463  * filter span's with no attributes out..
21464  * @constructor
21465  * Run a new Span Filter
21466  * @param {Object} config Configuration options
21467  */
21468
21469 Roo.htmleditor.FilterSpan = function(cfg)
21470 {
21471     // no need to apply config.
21472     this.walk(cfg.node);
21473 }
21474
21475 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21476 {
21477      
21478     tag : 'SPAN',
21479      
21480  
21481     replaceTag : function(node)
21482     {
21483         if (node.attributes && node.attributes.length > 0) {
21484             return true; // walk if there are any.
21485         }
21486         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21487         return false;
21488      
21489     }
21490     
21491 });/**
21492  * @class Roo.htmleditor.FilterTableWidth
21493   try and remove table width data - as that frequently messes up other stuff.
21494  * 
21495  *      was cleanTableWidths.
21496  *
21497  * Quite often pasting from word etc.. results in tables with column and widths.
21498  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21499  *
21500  * @constructor
21501  * Run a new Table Filter
21502  * @param {Object} config Configuration options
21503  */
21504
21505 Roo.htmleditor.FilterTableWidth = function(cfg)
21506 {
21507     // no need to apply config.
21508     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21509     this.walk(cfg.node);
21510 }
21511
21512 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21513 {
21514      
21515      
21516     
21517     replaceTag: function(node) {
21518         
21519         
21520       
21521         if (node.hasAttribute('width')) {
21522             node.removeAttribute('width');
21523         }
21524         
21525          
21526         if (node.hasAttribute("style")) {
21527             // pretty basic...
21528             
21529             var styles = node.getAttribute("style").split(";");
21530             var nstyle = [];
21531             Roo.each(styles, function(s) {
21532                 if (!s.match(/:/)) {
21533                     return;
21534                 }
21535                 var kv = s.split(":");
21536                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21537                     return;
21538                 }
21539                 // what ever is left... we allow.
21540                 nstyle.push(s);
21541             });
21542             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21543             if (!nstyle.length) {
21544                 node.removeAttribute('style');
21545             }
21546         }
21547         
21548         return true; // continue doing children..
21549     }
21550 });/**
21551  * @class Roo.htmleditor.FilterWord
21552  * try and clean up all the mess that Word generates.
21553  * 
21554  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21555  
21556  * @constructor
21557  * Run a new Span Filter
21558  * @param {Object} config Configuration options
21559  */
21560
21561 Roo.htmleditor.FilterWord = function(cfg)
21562 {
21563     // no need to apply config.
21564     this.walk(cfg.node);
21565 }
21566
21567 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21568 {
21569     tag: true,
21570      
21571     
21572     /**
21573      * Clean up MS wordisms...
21574      */
21575     replaceTag : function(node)
21576     {
21577          
21578         // no idea what this does - span with text, replaceds with just text.
21579         if(
21580                 node.nodeName == 'SPAN' &&
21581                 !node.hasAttributes() &&
21582                 node.childNodes.length == 1 &&
21583                 node.firstChild.nodeName == "#text"  
21584         ) {
21585             var textNode = node.firstChild;
21586             node.removeChild(textNode);
21587             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21588                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21589             }
21590             node.parentNode.insertBefore(textNode, node);
21591             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21592                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21593             }
21594             
21595             node.parentNode.removeChild(node);
21596             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21597         }
21598         
21599    
21600         
21601         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21602             node.parentNode.removeChild(node);
21603             return false; // dont do chidlren
21604         }
21605         //Roo.log(node.tagName);
21606         // remove - but keep children..
21607         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21608             //Roo.log('-- removed');
21609             while (node.childNodes.length) {
21610                 var cn = node.childNodes[0];
21611                 node.removeChild(cn);
21612                 node.parentNode.insertBefore(cn, node);
21613                 // move node to parent - and clean it..
21614                 this.replaceTag(cn);
21615             }
21616             node.parentNode.removeChild(node);
21617             /// no need to iterate chidlren = it's got none..
21618             //this.iterateChildren(node, this.cleanWord);
21619             return false; // no need to iterate children.
21620         }
21621         // clean styles
21622         if (node.className.length) {
21623             
21624             var cn = node.className.split(/\W+/);
21625             var cna = [];
21626             Roo.each(cn, function(cls) {
21627                 if (cls.match(/Mso[a-zA-Z]+/)) {
21628                     return;
21629                 }
21630                 cna.push(cls);
21631             });
21632             node.className = cna.length ? cna.join(' ') : '';
21633             if (!cna.length) {
21634                 node.removeAttribute("class");
21635             }
21636         }
21637         
21638         if (node.hasAttribute("lang")) {
21639             node.removeAttribute("lang");
21640         }
21641         
21642         if (node.hasAttribute("style")) {
21643             
21644             var styles = node.getAttribute("style").split(";");
21645             var nstyle = [];
21646             Roo.each(styles, function(s) {
21647                 if (!s.match(/:/)) {
21648                     return;
21649                 }
21650                 var kv = s.split(":");
21651                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21652                     return;
21653                 }
21654                 // what ever is left... we allow.
21655                 nstyle.push(s);
21656             });
21657             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21658             if (!nstyle.length) {
21659                 node.removeAttribute('style');
21660             }
21661         }
21662         return true; // do children
21663         
21664         
21665         
21666     }
21667 });
21668 /**
21669  * @class Roo.htmleditor.FilterStyleToTag
21670  * part of the word stuff... - certain 'styles' should be converted to tags.
21671  * eg.
21672  *   font-weight: bold -> bold
21673  *   ?? super / subscrit etc..
21674  * 
21675  * @constructor
21676 * Run a new style to tag filter.
21677 * @param {Object} config Configuration options
21678  */
21679 Roo.htmleditor.FilterStyleToTag = function(cfg)
21680 {
21681     
21682     this.tags = {
21683         B  : [ 'fontWeight' , 'bold'],
21684         I :  [ 'fontStyle' , 'italic'],
21685         //pre :  [ 'font-style' , 'italic'],
21686         // h1.. h6 ?? font-size?
21687         SUP : [ 'verticalAlign' , 'super' ],
21688         SUB : [ 'verticalAlign' , 'sub' ]
21689         
21690         
21691     };
21692     
21693     Roo.apply(this, cfg);
21694      
21695     
21696     this.walk(cfg.node);
21697     
21698     
21699     
21700 }
21701
21702
21703 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21704 {
21705     tag: true, // all tags
21706     
21707     tags : false,
21708     
21709     
21710     replaceTag : function(node)
21711     {
21712         
21713         
21714         if (node.getAttribute("style") === null) {
21715             return true;
21716         }
21717         var inject = [];
21718         for (var k in this.tags) {
21719             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21720                 inject.push(k);
21721                 node.style.removeProperty(this.tags[k][0]);
21722             }
21723         }
21724         if (!inject.length) {
21725             return true; 
21726         }
21727         var cn = Array.from(node.childNodes);
21728         var nn = node;
21729         Roo.each(inject, function(t) {
21730             var nc = node.ownerDocument.createElement(t);
21731             nn.appendChild(nc);
21732             nn = nc;
21733         });
21734         for(var i = 0;i < cn.length;cn++) {
21735             node.removeChild(cn[i]);
21736             nn.appendChild(cn[i]);
21737         }
21738         return true /// iterate thru
21739     }
21740     
21741 })/**
21742  * @class Roo.htmleditor.FilterLongBr
21743  * BR/BR/BR - keep a maximum of 2...
21744  * @constructor
21745  * Run a new Long BR Filter
21746  * @param {Object} config Configuration options
21747  */
21748
21749 Roo.htmleditor.FilterLongBr = function(cfg)
21750 {
21751     // no need to apply config.
21752     this.walk(cfg.node);
21753 }
21754
21755 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21756 {
21757     
21758      
21759     tag : 'BR',
21760     
21761      
21762     replaceTag : function(node)
21763     {
21764         
21765         var ps = node.nextSibling;
21766         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21767             ps = ps.nextSibling;
21768         }
21769         
21770         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21771             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21772             return false;
21773         }
21774         
21775         if (!ps || ps.nodeType != 1) {
21776             return false;
21777         }
21778         
21779         if (!ps || ps.tagName != 'BR') {
21780            
21781             return false;
21782         }
21783         
21784         
21785         
21786         
21787         
21788         if (!node.previousSibling) {
21789             return false;
21790         }
21791         var ps = node.previousSibling;
21792         
21793         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21794             ps = ps.previousSibling;
21795         }
21796         if (!ps || ps.nodeType != 1) {
21797             return false;
21798         }
21799         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21800         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21801             return false;
21802         }
21803         
21804         node.parentNode.removeChild(node); // remove me...
21805         
21806         return false; // no need to do children
21807
21808     }
21809     
21810 }); 
21811
21812 /**
21813  * @class Roo.htmleditor.FilterBlock
21814  * removes id / data-block and contenteditable that are associated with blocks
21815  * usage should be done on a cloned copy of the dom
21816  * @constructor
21817 * Run a new Attribute Filter { node : xxxx }}
21818 * @param {Object} config Configuration options
21819  */
21820 Roo.htmleditor.FilterBlock = function(cfg)
21821 {
21822     Roo.apply(this, cfg);
21823     var qa = cfg.node.querySelectorAll;
21824     this.removeAttributes('data-block');
21825     this.removeAttributes('contenteditable');
21826     this.removeAttributes('id');
21827     
21828 }
21829
21830 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21831 {
21832     node: true, // all tags
21833      
21834      
21835     removeAttributes : function(attr)
21836     {
21837         var ar = this.node.querySelectorAll('*[' + attr + ']');
21838         for (var i =0;i<ar.length;i++) {
21839             ar[i].removeAttribute(attr);
21840         }
21841     }
21842         
21843         
21844         
21845     
21846 });
21847 /***
21848  * This is based loosely on tinymce 
21849  * @class Roo.htmleditor.TidySerializer
21850  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21851  * @constructor
21852  * @method Serializer
21853  * @param {Object} settings Name/value settings object.
21854  */
21855
21856
21857 Roo.htmleditor.TidySerializer = function(settings)
21858 {
21859     Roo.apply(this, settings);
21860     
21861     this.writer = new Roo.htmleditor.TidyWriter(settings);
21862     
21863     
21864
21865 };
21866 Roo.htmleditor.TidySerializer.prototype = {
21867     
21868     /**
21869      * @param {boolean} inner do the inner of the node.
21870      */
21871     inner : false,
21872     
21873     writer : false,
21874     
21875     /**
21876     * Serializes the specified node into a string.
21877     *
21878     * @example
21879     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
21880     * @method serialize
21881     * @param {DomElement} node Node instance to serialize.
21882     * @return {String} String with HTML based on DOM tree.
21883     */
21884     serialize : function(node) {
21885         
21886         // = settings.validate;
21887         var writer = this.writer;
21888         var self  = this;
21889         this.handlers = {
21890             // #text
21891             3: function(node) {
21892                 
21893                 writer.text(node.nodeValue, node);
21894             },
21895             // #comment
21896             8: function(node) {
21897                 writer.comment(node.nodeValue);
21898             },
21899             // Processing instruction
21900             7: function(node) {
21901                 writer.pi(node.name, node.nodeValue);
21902             },
21903             // Doctype
21904             10: function(node) {
21905                 writer.doctype(node.nodeValue);
21906             },
21907             // CDATA
21908             4: function(node) {
21909                 writer.cdata(node.nodeValue);
21910             },
21911             // Document fragment
21912             11: function(node) {
21913                 node = node.firstChild;
21914                 if (!node) {
21915                     return;
21916                 }
21917                 while(node) {
21918                     self.walk(node);
21919                     node = node.nextSibling
21920                 }
21921             }
21922         };
21923         writer.reset();
21924         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
21925         return writer.getContent();
21926     },
21927
21928     walk: function(node)
21929     {
21930         var attrName, attrValue, sortedAttrs, i, l, elementRule,
21931             handler = this.handlers[node.nodeType];
21932             
21933         if (handler) {
21934             handler(node);
21935             return;
21936         }
21937     
21938         var name = node.nodeName;
21939         var isEmpty = node.childNodes.length < 1;
21940       
21941         var writer = this.writer;
21942         var attrs = node.attributes;
21943         // Sort attributes
21944         
21945         writer.start(node.nodeName, attrs, isEmpty, node);
21946         if (isEmpty) {
21947             return;
21948         }
21949         node = node.firstChild;
21950         if (!node) {
21951             writer.end(name);
21952             return;
21953         }
21954         while (node) {
21955             this.walk(node);
21956             node = node.nextSibling;
21957         }
21958         writer.end(name);
21959         
21960     
21961     }
21962     // Serialize element and treat all non elements as fragments
21963    
21964 }; 
21965
21966 /***
21967  * This is based loosely on tinymce 
21968  * @class Roo.htmleditor.TidyWriter
21969  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
21970  *
21971  * Known issues?
21972  * - not tested much with 'PRE' formated elements.
21973  * 
21974  *
21975  *
21976  */
21977
21978 Roo.htmleditor.TidyWriter = function(settings)
21979 {
21980     
21981     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
21982     Roo.apply(this, settings);
21983     this.html = [];
21984     this.state = [];
21985      
21986     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
21987   
21988 }
21989 Roo.htmleditor.TidyWriter.prototype = {
21990
21991  
21992     state : false,
21993     
21994     indent :  '  ',
21995     
21996     // part of state...
21997     indentstr : '',
21998     in_pre: false,
21999     in_inline : false,
22000     last_inline : false,
22001     encode : false,
22002      
22003     
22004             /**
22005     * Writes the a start element such as <p id="a">.
22006     *
22007     * @method start
22008     * @param {String} name Name of the element.
22009     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22010     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22011     */
22012     start: function(name, attrs, empty, node)
22013     {
22014         var i, l, attr, value;
22015         
22016         // there are some situations where adding line break && indentation will not work. will not work.
22017         // <span / b / i ... formating?
22018         
22019         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22020         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22021         
22022         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22023         
22024         var add_lb = name == 'BR' ? false : in_inline;
22025         
22026         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22027             i_inline = false;
22028         }
22029
22030         var indentstr =  this.indentstr;
22031         
22032         // e_inline = elements that can be inline, but still allow \n before and after?
22033         // only 'BR' ??? any others?
22034         
22035         // ADD LINE BEFORE tage
22036         if (!this.in_pre) {
22037             if (in_inline) {
22038                 //code
22039                 if (name == 'BR') {
22040                     this.addLine();
22041                 } else if (this.lastElementEndsWS()) {
22042                     this.addLine();
22043                 } else{
22044                     // otherwise - no new line. (and dont indent.)
22045                     indentstr = '';
22046                 }
22047                 
22048             } else {
22049                 this.addLine();
22050             }
22051         } else {
22052             indentstr = '';
22053         }
22054         
22055         this.html.push(indentstr + '<', name.toLowerCase());
22056         
22057         if (attrs) {
22058             for (i = 0, l = attrs.length; i < l; i++) {
22059                 attr = attrs[i];
22060                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22061             }
22062         }
22063      
22064         if (empty) {
22065             if (is_short) {
22066                 this.html[this.html.length] = '/>';
22067             } else {
22068                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22069             }
22070             var e_inline = name == 'BR' ? false : this.in_inline;
22071             
22072             if (!e_inline && !this.in_pre) {
22073                 this.addLine();
22074             }
22075             return;
22076         
22077         }
22078         // not empty..
22079         this.html[this.html.length] = '>';
22080         
22081         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22082         /*
22083         if (!in_inline && !in_pre) {
22084             var cn = node.firstChild;
22085             while(cn) {
22086                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22087                     in_inline = true
22088                     break;
22089                 }
22090                 cn = cn.nextSibling;
22091             }
22092              
22093         }
22094         */
22095         
22096         
22097         this.pushState({
22098             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
22099             in_pre : in_pre,
22100             in_inline :  in_inline
22101         });
22102         // add a line after if we are not in a
22103         
22104         if (!in_inline && !in_pre) {
22105             this.addLine();
22106         }
22107         
22108             
22109          
22110         
22111     },
22112     
22113     lastElementEndsWS : function()
22114     {
22115         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22116         if (value === false) {
22117             return true;
22118         }
22119         return value.match(/\s+$/);
22120         
22121     },
22122     
22123     /**
22124      * Writes the a end element such as </p>.
22125      *
22126      * @method end
22127      * @param {String} name Name of the element.
22128      */
22129     end: function(name) {
22130         var value;
22131         this.popState();
22132         var indentstr = '';
22133         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22134         
22135         if (!this.in_pre && !in_inline) {
22136             this.addLine();
22137             indentstr  = this.indentstr;
22138         }
22139         this.html.push(indentstr + '</', name.toLowerCase(), '>');
22140         this.last_inline = in_inline;
22141         
22142         // pop the indent state..
22143     },
22144     /**
22145      * Writes a text node.
22146      *
22147      * In pre - we should not mess with the contents.
22148      * 
22149      *
22150      * @method text
22151      * @param {String} text String to write out.
22152      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22153      */
22154     text: function(text, node)
22155     {
22156         // if not in whitespace critical
22157         if (text.length < 1) {
22158             return;
22159         }
22160         if (this.in_pre) {
22161             this.html[this.html.length] =  text;
22162             return;   
22163         }
22164         
22165         if (this.in_inline) {
22166             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22167             if (text != ' ') {
22168                 text = text.replace(/\s+/,' ');  // all white space to single white space
22169                 
22170                     
22171                 // if next tag is '<BR>', then we can trim right..
22172                 if (node.nextSibling &&
22173                     node.nextSibling.nodeType == 1 &&
22174                     node.nextSibling.nodeName == 'BR' )
22175                 {
22176                     text = text.replace(/\s+$/g,'');
22177                 }
22178                 // if previous tag was a BR, we can also trim..
22179                 if (node.previousSibling &&
22180                     node.previousSibling.nodeType == 1 &&
22181                     node.previousSibling.nodeName == 'BR' )
22182                 {
22183                     text = this.indentstr +  text.replace(/^\s+/g,'');
22184                 }
22185                 if (text.match(/\n/)) {
22186                     text = text.replace(
22187                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22188                     );
22189                     // remoeve the last whitespace / line break.
22190                     text = text.replace(/\n\s+$/,'');
22191                 }
22192                 // repace long lines
22193                 
22194             }
22195              
22196             this.html[this.html.length] =  text;
22197             return;   
22198         }
22199         // see if previous element was a inline element.
22200         var indentstr = this.indentstr;
22201    
22202         text = text.replace(/\s+/g," "); // all whitespace into single white space.
22203         
22204         // should trim left?
22205         if (node.previousSibling &&
22206             node.previousSibling.nodeType == 1 &&
22207             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22208         {
22209             indentstr = '';
22210             
22211         } else {
22212             this.addLine();
22213             text = text.replace(/^\s+/,''); // trim left
22214           
22215         }
22216         // should trim right?
22217         if (node.nextSibling &&
22218             node.nextSibling.nodeType == 1 &&
22219             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22220         {
22221           // noop
22222             
22223         }  else {
22224             text = text.replace(/\s+$/,''); // trim right
22225         }
22226          
22227               
22228         
22229         
22230         
22231         if (text.length < 1) {
22232             return;
22233         }
22234         if (!text.match(/\n/)) {
22235             this.html.push(indentstr + text);
22236             return;
22237         }
22238         
22239         text = this.indentstr + text.replace(
22240             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22241         );
22242         // remoeve the last whitespace / line break.
22243         text = text.replace(/\s+$/,''); 
22244         
22245         this.html.push(text);
22246         
22247         // split and indent..
22248         
22249         
22250     },
22251     /**
22252      * Writes a cdata node such as <![CDATA[data]]>.
22253      *
22254      * @method cdata
22255      * @param {String} text String to write out inside the cdata.
22256      */
22257     cdata: function(text) {
22258         this.html.push('<![CDATA[', text, ']]>');
22259     },
22260     /**
22261     * Writes a comment node such as <!-- Comment -->.
22262     *
22263     * @method cdata
22264     * @param {String} text String to write out inside the comment.
22265     */
22266    comment: function(text) {
22267        this.html.push('<!--', text, '-->');
22268    },
22269     /**
22270      * Writes a PI node such as <?xml attr="value" ?>.
22271      *
22272      * @method pi
22273      * @param {String} name Name of the pi.
22274      * @param {String} text String to write out inside the pi.
22275      */
22276     pi: function(name, text) {
22277         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22278         this.indent != '' && this.html.push('\n');
22279     },
22280     /**
22281      * Writes a doctype node such as <!DOCTYPE data>.
22282      *
22283      * @method doctype
22284      * @param {String} text String to write out inside the doctype.
22285      */
22286     doctype: function(text) {
22287         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22288     },
22289     /**
22290      * Resets the internal buffer if one wants to reuse the writer.
22291      *
22292      * @method reset
22293      */
22294     reset: function() {
22295         this.html.length = 0;
22296         this.state = [];
22297         this.pushState({
22298             indentstr : '',
22299             in_pre : false, 
22300             in_inline : false
22301         })
22302     },
22303     /**
22304      * Returns the contents that got serialized.
22305      *
22306      * @method getContent
22307      * @return {String} HTML contents that got written down.
22308      */
22309     getContent: function() {
22310         return this.html.join('').replace(/\n$/, '');
22311     },
22312     
22313     pushState : function(cfg)
22314     {
22315         this.state.push(cfg);
22316         Roo.apply(this, cfg);
22317     },
22318     
22319     popState : function()
22320     {
22321         if (this.state.length < 1) {
22322             return; // nothing to push
22323         }
22324         var cfg = {
22325             in_pre: false,
22326             indentstr : ''
22327         };
22328         this.state.pop();
22329         if (this.state.length > 0) {
22330             cfg = this.state[this.state.length-1]; 
22331         }
22332         Roo.apply(this, cfg);
22333     },
22334     
22335     addLine: function()
22336     {
22337         if (this.html.length < 1) {
22338             return;
22339         }
22340         
22341         
22342         var value = this.html[this.html.length - 1];
22343         if (value.length > 0 && '\n' !== value) {
22344             this.html.push('\n');
22345         }
22346     }
22347     
22348     
22349 //'pre script noscript style textarea video audio iframe object code'
22350 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
22351 // inline 
22352 };
22353
22354 Roo.htmleditor.TidyWriter.inline_elements = [
22355         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22356         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
22357 ];
22358 Roo.htmleditor.TidyWriter.shortend_elements = [
22359     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22360     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22361 ];
22362
22363 Roo.htmleditor.TidyWriter.whitespace_elements = [
22364     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22365 ];/***
22366  * This is based loosely on tinymce 
22367  * @class Roo.htmleditor.TidyEntities
22368  * @static
22369  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22370  *
22371  * Not 100% sure this is actually used or needed.
22372  */
22373
22374 Roo.htmleditor.TidyEntities = {
22375     
22376     /**
22377      * initialize data..
22378      */
22379     init : function (){
22380      
22381         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22382        
22383     },
22384
22385
22386     buildEntitiesLookup: function(items, radix) {
22387         var i, chr, entity, lookup = {};
22388         if (!items) {
22389             return {};
22390         }
22391         items = typeof(items) == 'string' ? items.split(',') : items;
22392         radix = radix || 10;
22393         // Build entities lookup table
22394         for (i = 0; i < items.length; i += 2) {
22395             chr = String.fromCharCode(parseInt(items[i], radix));
22396             // Only add non base entities
22397             if (!this.baseEntities[chr]) {
22398                 entity = '&' + items[i + 1] + ';';
22399                 lookup[chr] = entity;
22400                 lookup[entity] = chr;
22401             }
22402         }
22403         return lookup;
22404         
22405     },
22406     
22407     asciiMap : {
22408             128: '€',
22409             130: '‚',
22410             131: 'ƒ',
22411             132: '„',
22412             133: '…',
22413             134: '†',
22414             135: '‡',
22415             136: 'ˆ',
22416             137: '‰',
22417             138: 'Š',
22418             139: '‹',
22419             140: 'Œ',
22420             142: 'Ž',
22421             145: '‘',
22422             146: '’',
22423             147: '“',
22424             148: '”',
22425             149: '•',
22426             150: '–',
22427             151: '—',
22428             152: '˜',
22429             153: '™',
22430             154: 'š',
22431             155: '›',
22432             156: 'œ',
22433             158: 'ž',
22434             159: 'Ÿ'
22435     },
22436     // Raw entities
22437     baseEntities : {
22438         '"': '&quot;',
22439         // Needs to be escaped since the YUI compressor would otherwise break the code
22440         '\'': '&#39;',
22441         '<': '&lt;',
22442         '>': '&gt;',
22443         '&': '&amp;',
22444         '`': '&#96;'
22445     },
22446     // Reverse lookup table for raw entities
22447     reverseEntities : {
22448         '&lt;': '<',
22449         '&gt;': '>',
22450         '&amp;': '&',
22451         '&quot;': '"',
22452         '&apos;': '\''
22453     },
22454     
22455     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22456     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22457     rawCharsRegExp : /[<>&\"\']/g,
22458     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22459     namedEntities  : false,
22460     namedEntitiesData : [ 
22461         '50',
22462         'nbsp',
22463         '51',
22464         'iexcl',
22465         '52',
22466         'cent',
22467         '53',
22468         'pound',
22469         '54',
22470         'curren',
22471         '55',
22472         'yen',
22473         '56',
22474         'brvbar',
22475         '57',
22476         'sect',
22477         '58',
22478         'uml',
22479         '59',
22480         'copy',
22481         '5a',
22482         'ordf',
22483         '5b',
22484         'laquo',
22485         '5c',
22486         'not',
22487         '5d',
22488         'shy',
22489         '5e',
22490         'reg',
22491         '5f',
22492         'macr',
22493         '5g',
22494         'deg',
22495         '5h',
22496         'plusmn',
22497         '5i',
22498         'sup2',
22499         '5j',
22500         'sup3',
22501         '5k',
22502         'acute',
22503         '5l',
22504         'micro',
22505         '5m',
22506         'para',
22507         '5n',
22508         'middot',
22509         '5o',
22510         'cedil',
22511         '5p',
22512         'sup1',
22513         '5q',
22514         'ordm',
22515         '5r',
22516         'raquo',
22517         '5s',
22518         'frac14',
22519         '5t',
22520         'frac12',
22521         '5u',
22522         'frac34',
22523         '5v',
22524         'iquest',
22525         '60',
22526         'Agrave',
22527         '61',
22528         'Aacute',
22529         '62',
22530         'Acirc',
22531         '63',
22532         'Atilde',
22533         '64',
22534         'Auml',
22535         '65',
22536         'Aring',
22537         '66',
22538         'AElig',
22539         '67',
22540         'Ccedil',
22541         '68',
22542         'Egrave',
22543         '69',
22544         'Eacute',
22545         '6a',
22546         'Ecirc',
22547         '6b',
22548         'Euml',
22549         '6c',
22550         'Igrave',
22551         '6d',
22552         'Iacute',
22553         '6e',
22554         'Icirc',
22555         '6f',
22556         'Iuml',
22557         '6g',
22558         'ETH',
22559         '6h',
22560         'Ntilde',
22561         '6i',
22562         'Ograve',
22563         '6j',
22564         'Oacute',
22565         '6k',
22566         'Ocirc',
22567         '6l',
22568         'Otilde',
22569         '6m',
22570         'Ouml',
22571         '6n',
22572         'times',
22573         '6o',
22574         'Oslash',
22575         '6p',
22576         'Ugrave',
22577         '6q',
22578         'Uacute',
22579         '6r',
22580         'Ucirc',
22581         '6s',
22582         'Uuml',
22583         '6t',
22584         'Yacute',
22585         '6u',
22586         'THORN',
22587         '6v',
22588         'szlig',
22589         '70',
22590         'agrave',
22591         '71',
22592         'aacute',
22593         '72',
22594         'acirc',
22595         '73',
22596         'atilde',
22597         '74',
22598         'auml',
22599         '75',
22600         'aring',
22601         '76',
22602         'aelig',
22603         '77',
22604         'ccedil',
22605         '78',
22606         'egrave',
22607         '79',
22608         'eacute',
22609         '7a',
22610         'ecirc',
22611         '7b',
22612         'euml',
22613         '7c',
22614         'igrave',
22615         '7d',
22616         'iacute',
22617         '7e',
22618         'icirc',
22619         '7f',
22620         'iuml',
22621         '7g',
22622         'eth',
22623         '7h',
22624         'ntilde',
22625         '7i',
22626         'ograve',
22627         '7j',
22628         'oacute',
22629         '7k',
22630         'ocirc',
22631         '7l',
22632         'otilde',
22633         '7m',
22634         'ouml',
22635         '7n',
22636         'divide',
22637         '7o',
22638         'oslash',
22639         '7p',
22640         'ugrave',
22641         '7q',
22642         'uacute',
22643         '7r',
22644         'ucirc',
22645         '7s',
22646         'uuml',
22647         '7t',
22648         'yacute',
22649         '7u',
22650         'thorn',
22651         '7v',
22652         'yuml',
22653         'ci',
22654         'fnof',
22655         'sh',
22656         'Alpha',
22657         'si',
22658         'Beta',
22659         'sj',
22660         'Gamma',
22661         'sk',
22662         'Delta',
22663         'sl',
22664         'Epsilon',
22665         'sm',
22666         'Zeta',
22667         'sn',
22668         'Eta',
22669         'so',
22670         'Theta',
22671         'sp',
22672         'Iota',
22673         'sq',
22674         'Kappa',
22675         'sr',
22676         'Lambda',
22677         'ss',
22678         'Mu',
22679         'st',
22680         'Nu',
22681         'su',
22682         'Xi',
22683         'sv',
22684         'Omicron',
22685         't0',
22686         'Pi',
22687         't1',
22688         'Rho',
22689         't3',
22690         'Sigma',
22691         't4',
22692         'Tau',
22693         't5',
22694         'Upsilon',
22695         't6',
22696         'Phi',
22697         't7',
22698         'Chi',
22699         't8',
22700         'Psi',
22701         't9',
22702         'Omega',
22703         'th',
22704         'alpha',
22705         'ti',
22706         'beta',
22707         'tj',
22708         'gamma',
22709         'tk',
22710         'delta',
22711         'tl',
22712         'epsilon',
22713         'tm',
22714         'zeta',
22715         'tn',
22716         'eta',
22717         'to',
22718         'theta',
22719         'tp',
22720         'iota',
22721         'tq',
22722         'kappa',
22723         'tr',
22724         'lambda',
22725         'ts',
22726         'mu',
22727         'tt',
22728         'nu',
22729         'tu',
22730         'xi',
22731         'tv',
22732         'omicron',
22733         'u0',
22734         'pi',
22735         'u1',
22736         'rho',
22737         'u2',
22738         'sigmaf',
22739         'u3',
22740         'sigma',
22741         'u4',
22742         'tau',
22743         'u5',
22744         'upsilon',
22745         'u6',
22746         'phi',
22747         'u7',
22748         'chi',
22749         'u8',
22750         'psi',
22751         'u9',
22752         'omega',
22753         'uh',
22754         'thetasym',
22755         'ui',
22756         'upsih',
22757         'um',
22758         'piv',
22759         '812',
22760         'bull',
22761         '816',
22762         'hellip',
22763         '81i',
22764         'prime',
22765         '81j',
22766         'Prime',
22767         '81u',
22768         'oline',
22769         '824',
22770         'frasl',
22771         '88o',
22772         'weierp',
22773         '88h',
22774         'image',
22775         '88s',
22776         'real',
22777         '892',
22778         'trade',
22779         '89l',
22780         'alefsym',
22781         '8cg',
22782         'larr',
22783         '8ch',
22784         'uarr',
22785         '8ci',
22786         'rarr',
22787         '8cj',
22788         'darr',
22789         '8ck',
22790         'harr',
22791         '8dl',
22792         'crarr',
22793         '8eg',
22794         'lArr',
22795         '8eh',
22796         'uArr',
22797         '8ei',
22798         'rArr',
22799         '8ej',
22800         'dArr',
22801         '8ek',
22802         'hArr',
22803         '8g0',
22804         'forall',
22805         '8g2',
22806         'part',
22807         '8g3',
22808         'exist',
22809         '8g5',
22810         'empty',
22811         '8g7',
22812         'nabla',
22813         '8g8',
22814         'isin',
22815         '8g9',
22816         'notin',
22817         '8gb',
22818         'ni',
22819         '8gf',
22820         'prod',
22821         '8gh',
22822         'sum',
22823         '8gi',
22824         'minus',
22825         '8gn',
22826         'lowast',
22827         '8gq',
22828         'radic',
22829         '8gt',
22830         'prop',
22831         '8gu',
22832         'infin',
22833         '8h0',
22834         'ang',
22835         '8h7',
22836         'and',
22837         '8h8',
22838         'or',
22839         '8h9',
22840         'cap',
22841         '8ha',
22842         'cup',
22843         '8hb',
22844         'int',
22845         '8hk',
22846         'there4',
22847         '8hs',
22848         'sim',
22849         '8i5',
22850         'cong',
22851         '8i8',
22852         'asymp',
22853         '8j0',
22854         'ne',
22855         '8j1',
22856         'equiv',
22857         '8j4',
22858         'le',
22859         '8j5',
22860         'ge',
22861         '8k2',
22862         'sub',
22863         '8k3',
22864         'sup',
22865         '8k4',
22866         'nsub',
22867         '8k6',
22868         'sube',
22869         '8k7',
22870         'supe',
22871         '8kl',
22872         'oplus',
22873         '8kn',
22874         'otimes',
22875         '8l5',
22876         'perp',
22877         '8m5',
22878         'sdot',
22879         '8o8',
22880         'lceil',
22881         '8o9',
22882         'rceil',
22883         '8oa',
22884         'lfloor',
22885         '8ob',
22886         'rfloor',
22887         '8p9',
22888         'lang',
22889         '8pa',
22890         'rang',
22891         '9ea',
22892         'loz',
22893         '9j0',
22894         'spades',
22895         '9j3',
22896         'clubs',
22897         '9j5',
22898         'hearts',
22899         '9j6',
22900         'diams',
22901         'ai',
22902         'OElig',
22903         'aj',
22904         'oelig',
22905         'b0',
22906         'Scaron',
22907         'b1',
22908         'scaron',
22909         'bo',
22910         'Yuml',
22911         'm6',
22912         'circ',
22913         'ms',
22914         'tilde',
22915         '802',
22916         'ensp',
22917         '803',
22918         'emsp',
22919         '809',
22920         'thinsp',
22921         '80c',
22922         'zwnj',
22923         '80d',
22924         'zwj',
22925         '80e',
22926         'lrm',
22927         '80f',
22928         'rlm',
22929         '80j',
22930         'ndash',
22931         '80k',
22932         'mdash',
22933         '80o',
22934         'lsquo',
22935         '80p',
22936         'rsquo',
22937         '80q',
22938         'sbquo',
22939         '80s',
22940         'ldquo',
22941         '80t',
22942         'rdquo',
22943         '80u',
22944         'bdquo',
22945         '810',
22946         'dagger',
22947         '811',
22948         'Dagger',
22949         '81g',
22950         'permil',
22951         '81p',
22952         'lsaquo',
22953         '81q',
22954         'rsaquo',
22955         '85c',
22956         'euro'
22957     ],
22958
22959          
22960     /**
22961      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
22962      *
22963      * @method encodeRaw
22964      * @param {String} text Text to encode.
22965      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22966      * @return {String} Entity encoded text.
22967      */
22968     encodeRaw: function(text, attr)
22969     {
22970         var t = this;
22971         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
22972             return t.baseEntities[chr] || chr;
22973         });
22974     },
22975     /**
22976      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
22977      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
22978      * and is exposed as the DOMUtils.encode function.
22979      *
22980      * @method encodeAllRaw
22981      * @param {String} text Text to encode.
22982      * @return {String} Entity encoded text.
22983      */
22984     encodeAllRaw: function(text) {
22985         var t = this;
22986         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
22987             return t.baseEntities[chr] || chr;
22988         });
22989     },
22990     /**
22991      * Encodes the specified string using numeric entities. The core entities will be
22992      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
22993      *
22994      * @method encodeNumeric
22995      * @param {String} text Text to encode.
22996      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
22997      * @return {String} Entity encoded text.
22998      */
22999     encodeNumeric: function(text, attr) {
23000         var t = this;
23001         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23002             // Multi byte sequence convert it to a single entity
23003             if (chr.length > 1) {
23004                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23005             }
23006             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23007         });
23008     },
23009     /**
23010      * Encodes the specified string using named entities. The core entities will be encoded
23011      * as named ones but all non lower ascii characters will be encoded into named entities.
23012      *
23013      * @method encodeNamed
23014      * @param {String} text Text to encode.
23015      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23016      * @param {Object} entities Optional parameter with entities to use.
23017      * @return {String} Entity encoded text.
23018      */
23019     encodeNamed: function(text, attr, entities) {
23020         var t = this;
23021         entities = entities || this.namedEntities;
23022         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23023             return t.baseEntities[chr] || entities[chr] || chr;
23024         });
23025     },
23026     /**
23027      * Returns an encode function based on the name(s) and it's optional entities.
23028      *
23029      * @method getEncodeFunc
23030      * @param {String} name Comma separated list of encoders for example named,numeric.
23031      * @param {String} entities Optional parameter with entities to use instead of the built in set.
23032      * @return {function} Encode function to be used.
23033      */
23034     getEncodeFunc: function(name, entities) {
23035         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23036         var t = this;
23037         function encodeNamedAndNumeric(text, attr) {
23038             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23039                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23040             });
23041         }
23042
23043         function encodeCustomNamed(text, attr) {
23044             return t.encodeNamed(text, attr, entities);
23045         }
23046         // Replace + with , to be compatible with previous TinyMCE versions
23047         name = this.makeMap(name.replace(/\+/g, ','));
23048         // Named and numeric encoder
23049         if (name.named && name.numeric) {
23050             return this.encodeNamedAndNumeric;
23051         }
23052         // Named encoder
23053         if (name.named) {
23054             // Custom names
23055             if (entities) {
23056                 return encodeCustomNamed;
23057             }
23058             return this.encodeNamed;
23059         }
23060         // Numeric
23061         if (name.numeric) {
23062             return this.encodeNumeric;
23063         }
23064         // Raw encoder
23065         return this.encodeRaw;
23066     },
23067     /**
23068      * Decodes the specified string, this will replace entities with raw UTF characters.
23069      *
23070      * @method decode
23071      * @param {String} text Text to entity decode.
23072      * @return {String} Entity decoded string.
23073      */
23074     decode: function(text)
23075     {
23076         var  t = this;
23077         return text.replace(this.entityRegExp, function(all, numeric) {
23078             if (numeric) {
23079                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23080                 // Support upper UTF
23081                 if (numeric > 65535) {
23082                     numeric -= 65536;
23083                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23084                 }
23085                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23086             }
23087             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23088         });
23089     },
23090     nativeDecode : function (text) {
23091         return text;
23092     },
23093     makeMap : function (items, delim, map) {
23094                 var i;
23095                 items = items || [];
23096                 delim = delim || ',';
23097                 if (typeof items == "string") {
23098                         items = items.split(delim);
23099                 }
23100                 map = map || {};
23101                 i = items.length;
23102                 while (i--) {
23103                         map[items[i]] = {};
23104                 }
23105                 return map;
23106         }
23107 };
23108     
23109     
23110     
23111 Roo.htmleditor.TidyEntities.init();
23112 /**
23113  * @class Roo.htmleditor.KeyEnter
23114  * Handle Enter press..
23115  * @cfg {Roo.HtmlEditorCore} core the editor.
23116  * @constructor
23117  * Create a new Filter.
23118  * @param {Object} config Configuration options
23119  */
23120
23121
23122
23123
23124
23125 Roo.htmleditor.KeyEnter = function(cfg) {
23126     Roo.apply(this, cfg);
23127     // this does not actually call walk as it's really just a abstract class
23128  
23129     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23130 }
23131
23132 //Roo.htmleditor.KeyEnter.i = 0;
23133
23134
23135 Roo.htmleditor.KeyEnter.prototype = {
23136     
23137     core : false,
23138     
23139     keypress : function(e)
23140     {
23141         if (e.charCode != 13 && e.charCode != 10) {
23142             Roo.log([e.charCode,e]);
23143             return true;
23144         }
23145         e.preventDefault();
23146         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23147         var doc = this.core.doc;
23148           //add a new line
23149        
23150     
23151         var sel = this.core.getSelection();
23152         var range = sel.getRangeAt(0);
23153         var n = range.commonAncestorContainer;
23154         var pc = range.closest([ 'ol', 'ul']);
23155         var pli = range.closest('li');
23156         if (!pc || e.ctrlKey) {
23157             sel.insertNode('br', 'after'); 
23158          
23159             this.core.undoManager.addEvent();
23160             this.core.fireEditorEvent(e);
23161             return false;
23162         }
23163         
23164         // deal with <li> insetion
23165         if (pli.innerText.trim() == '' &&
23166             pli.previousSibling &&
23167             pli.previousSibling.nodeName == 'LI' &&
23168             pli.previousSibling.innerText.trim() ==  '') {
23169             pli.parentNode.removeChild(pli.previousSibling);
23170             sel.cursorAfter(pc);
23171             this.core.undoManager.addEvent();
23172             this.core.fireEditorEvent(e);
23173             return false;
23174         }
23175     
23176         var li = doc.createElement('LI');
23177         li.innerHTML = '&nbsp;';
23178         if (!pli || !pli.firstSibling) {
23179             pc.appendChild(li);
23180         } else {
23181             pli.parentNode.insertBefore(li, pli.firstSibling);
23182         }
23183         sel.cursorText (li.firstChild);
23184       
23185         this.core.undoManager.addEvent();
23186         this.core.fireEditorEvent(e);
23187
23188         return false;
23189         
23190     
23191         
23192         
23193          
23194     }
23195 };
23196      
23197 /**
23198  * @class Roo.htmleditor.Block
23199  * Base class for html editor blocks - do not use it directly .. extend it..
23200  * @cfg {DomElement} node The node to apply stuff to.
23201  * @cfg {String} friendly_name the name that appears in the context bar about this block
23202  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23203  
23204  * @constructor
23205  * Create a new Filter.
23206  * @param {Object} config Configuration options
23207  */
23208
23209 Roo.htmleditor.Block  = function(cfg)
23210 {
23211     // do nothing .. should not be called really.
23212 }
23213 /**
23214  * factory method to get the block from an element (using cache if necessary)
23215  * @static
23216  * @param {HtmlElement} the dom element
23217  */
23218 Roo.htmleditor.Block.factory = function(node)
23219 {
23220     var cc = Roo.htmleditor.Block.cache;
23221     var id = Roo.get(node).id;
23222     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23223         Roo.htmleditor.Block.cache[id].readElement(node);
23224         return Roo.htmleditor.Block.cache[id];
23225     }
23226     var db  = node.getAttribute('data-block');
23227     if (!db) {
23228         db = node.nodeName.toLowerCase().toUpperCaseFirst();
23229     }
23230     var cls = Roo.htmleditor['Block' + db];
23231     if (typeof(cls) == 'undefined') {
23232         //Roo.log(node.getAttribute('data-block'));
23233         Roo.log("OOps missing block : " + 'Block' + db);
23234         return false;
23235     }
23236     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23237     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
23238 };
23239
23240 /**
23241  * initalize all Elements from content that are 'blockable'
23242  * @static
23243  * @param the body element
23244  */
23245 Roo.htmleditor.Block.initAll = function(body, type)
23246 {
23247     if (typeof(type) == 'undefined') {
23248         var ia = Roo.htmleditor.Block.initAll;
23249         ia(body,'table');
23250         ia(body,'td');
23251         ia(body,'figure');
23252         return;
23253     }
23254     Roo.each(Roo.get(body).query(type), function(e) {
23255         Roo.htmleditor.Block.factory(e);    
23256     },this);
23257 };
23258 // question goes here... do we need to clear out this cache sometimes?
23259 // or show we make it relivant to the htmleditor.
23260 Roo.htmleditor.Block.cache = {};
23261
23262 Roo.htmleditor.Block.prototype = {
23263     
23264     node : false,
23265     
23266      // used by context menu
23267     friendly_name : 'Based Block',
23268     
23269     // text for button to delete this element
23270     deleteTitle : false,
23271     
23272     context : false,
23273     /**
23274      * Update a node with values from this object
23275      * @param {DomElement} node
23276      */
23277     updateElement : function(node)
23278     {
23279         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23280     },
23281      /**
23282      * convert to plain HTML for calling insertAtCursor..
23283      */
23284     toHTML : function()
23285     {
23286         return Roo.DomHelper.markup(this.toObject());
23287     },
23288     /**
23289      * used by readEleemnt to extract data from a node
23290      * may need improving as it's pretty basic
23291      
23292      * @param {DomElement} node
23293      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23294      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
23295      * @param {String} style the style property - eg. text-align
23296      */
23297     getVal : function(node, tag, attr, style)
23298     {
23299         var n = node;
23300         if (tag !== true && n.tagName != tag.toUpperCase()) {
23301             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23302             // but kiss for now.
23303             n = node.getElementsByTagName(tag).item(0);
23304         }
23305         if (!n) {
23306             return '';
23307         }
23308         if (attr === false) {
23309             return n;
23310         }
23311         if (attr == 'html') {
23312             return n.innerHTML;
23313         }
23314         if (attr == 'style') {
23315             return n.style[style]; 
23316         }
23317         
23318         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23319             
23320     },
23321     /**
23322      * create a DomHelper friendly object - for use with 
23323      * Roo.DomHelper.markup / overwrite / etc..
23324      * (override this)
23325      */
23326     toObject : function()
23327     {
23328         return {};
23329     },
23330       /**
23331      * Read a node that has a 'data-block' property - and extract the values from it.
23332      * @param {DomElement} node - the node
23333      */
23334     readElement : function(node)
23335     {
23336         
23337     } 
23338     
23339     
23340 };
23341
23342  
23343
23344 /**
23345  * @class Roo.htmleditor.BlockFigure
23346  * Block that has an image and a figcaption
23347  * @cfg {String} image_src the url for the image
23348  * @cfg {String} align (left|right) alignment for the block default left
23349  * @cfg {String} caption the text to appear below  (and in the alt tag)
23350  * @cfg {String} caption_display (block|none) display or not the caption
23351  * @cfg {String|number} image_width the width of the image number or %?
23352  * @cfg {String|number} image_height the height of the image number or %?
23353  * 
23354  * @constructor
23355  * Create a new Filter.
23356  * @param {Object} config Configuration options
23357  */
23358
23359 Roo.htmleditor.BlockFigure = function(cfg)
23360 {
23361     if (cfg.node) {
23362         this.readElement(cfg.node);
23363         this.updateElement(cfg.node);
23364     }
23365     Roo.apply(this, cfg);
23366 }
23367 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23368  
23369     
23370     // setable values.
23371     image_src: '',
23372     align: 'center',
23373     caption : '',
23374     caption_display : 'block',
23375     width : '100%',
23376     cls : '',
23377     href: '',
23378     video_url : '',
23379     
23380     // margin: '2%', not used
23381     
23382     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
23383
23384     
23385     // used by context menu
23386     friendly_name : 'Image with caption',
23387     deleteTitle : "Delete Image and Caption",
23388     
23389     contextMenu : function(toolbar)
23390     {
23391         
23392         var block = function() {
23393             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23394         };
23395         
23396         
23397         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23398         
23399         var syncValue = toolbar.editorcore.syncValue;
23400         
23401         var fields = {};
23402         
23403         return [
23404              {
23405                 xtype : 'TextItem',
23406                 text : "Source: ",
23407                 xns : rooui.Toolbar  //Boostrap?
23408             },
23409             {
23410                 xtype : 'Button',
23411                 text: 'Change Image URL',
23412                  
23413                 listeners : {
23414                     click: function (btn, state)
23415                     {
23416                         var b = block();
23417                         
23418                         Roo.MessageBox.show({
23419                             title : "Image Source URL",
23420                             msg : "Enter the url for the image",
23421                             buttons: Roo.MessageBox.OKCANCEL,
23422                             fn: function(btn, val){
23423                                 if (btn != 'ok') {
23424                                     return;
23425                                 }
23426                                 b.image_src = val;
23427                                 b.updateElement();
23428                                 syncValue();
23429                                 toolbar.editorcore.onEditorEvent();
23430                             },
23431                             minWidth:250,
23432                             prompt:true,
23433                             //multiline: multiline,
23434                             modal : true,
23435                             value : b.image_src
23436                         });
23437                     }
23438                 },
23439                 xns : rooui.Toolbar
23440             },
23441          
23442             {
23443                 xtype : 'Button',
23444                 text: 'Change Link URL',
23445                  
23446                 listeners : {
23447                     click: function (btn, state)
23448                     {
23449                         var b = block();
23450                         
23451                         Roo.MessageBox.show({
23452                             title : "Link URL",
23453                             msg : "Enter the url for the link - leave blank to have no link",
23454                             buttons: Roo.MessageBox.OKCANCEL,
23455                             fn: function(btn, val){
23456                                 if (btn != 'ok') {
23457                                     return;
23458                                 }
23459                                 b.href = val;
23460                                 b.updateElement();
23461                                 syncValue();
23462                                 toolbar.editorcore.onEditorEvent();
23463                             },
23464                             minWidth:250,
23465                             prompt:true,
23466                             //multiline: multiline,
23467                             modal : true,
23468                             value : b.href
23469                         });
23470                     }
23471                 },
23472                 xns : rooui.Toolbar
23473             },
23474             {
23475                 xtype : 'Button',
23476                 text: 'Show Video URL',
23477                  
23478                 listeners : {
23479                     click: function (btn, state)
23480                     {
23481                         Roo.MessageBox.alert("Video URL",
23482                             block().video_url == '' ? 'This image is not linked ot a video' :
23483                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23484                     }
23485                 },
23486                 xns : rooui.Toolbar
23487             },
23488             
23489             
23490             {
23491                 xtype : 'TextItem',
23492                 text : "Width: ",
23493                 xns : rooui.Toolbar  //Boostrap?
23494             },
23495             {
23496                 xtype : 'ComboBox',
23497                 allowBlank : false,
23498                 displayField : 'val',
23499                 editable : true,
23500                 listWidth : 100,
23501                 triggerAction : 'all',
23502                 typeAhead : true,
23503                 valueField : 'val',
23504                 width : 70,
23505                 name : 'width',
23506                 listeners : {
23507                     select : function (combo, r, index)
23508                     {
23509                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23510                         var b = block();
23511                         b.width = r.get('val');
23512                         b.updateElement();
23513                         syncValue();
23514                         toolbar.editorcore.onEditorEvent();
23515                     }
23516                 },
23517                 xns : rooui.form,
23518                 store : {
23519                     xtype : 'SimpleStore',
23520                     data : [
23521                         ['auto'],
23522                         ['50%'],
23523                         ['80%'],
23524                         ['100%']
23525                     ],
23526                     fields : [ 'val'],
23527                     xns : Roo.data
23528                 }
23529             },
23530             {
23531                 xtype : 'TextItem',
23532                 text : "Align: ",
23533                 xns : rooui.Toolbar  //Boostrap?
23534             },
23535             {
23536                 xtype : 'ComboBox',
23537                 allowBlank : false,
23538                 displayField : 'val',
23539                 editable : true,
23540                 listWidth : 100,
23541                 triggerAction : 'all',
23542                 typeAhead : true,
23543                 valueField : 'val',
23544                 width : 70,
23545                 name : 'align',
23546                 listeners : {
23547                     select : function (combo, r, index)
23548                     {
23549                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23550                         var b = block();
23551                         b.align = r.get('val');
23552                         b.updateElement();
23553                         syncValue();
23554                         toolbar.editorcore.onEditorEvent();
23555                     }
23556                 },
23557                 xns : rooui.form,
23558                 store : {
23559                     xtype : 'SimpleStore',
23560                     data : [
23561                         ['left'],
23562                         ['right'],
23563                         ['center']
23564                     ],
23565                     fields : [ 'val'],
23566                     xns : Roo.data
23567                 }
23568             },
23569             
23570             
23571             {
23572                 xtype : 'Button',
23573                 text: 'Hide Caption',
23574                 name : 'caption_display',
23575                 pressed : false,
23576                 enableToggle : true,
23577                 setValue : function(v) {
23578                     // this trigger toggle.
23579                     
23580                     var b  = block();
23581                     this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23582                     this.setPressed(b.caption_display == 'block' ? false: true);
23583                 },
23584                 listeners : {
23585                     toggle: function (btn, state)
23586                     {
23587                         var b  = block();
23588                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23589                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23590                         b.updateElement();
23591                         syncValue();
23592                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23593                         toolbar.editorcore.onEditorEvent();
23594                     }
23595                 },
23596                 xns : rooui.Toolbar
23597             }
23598         ];
23599         
23600     },
23601     /**
23602      * create a DomHelper friendly object - for use with
23603      * Roo.DomHelper.markup / overwrite / etc..
23604      */
23605     toObject : function()
23606     {
23607         var d = document.createElement('div');
23608         d.innerHTML = this.caption;
23609         
23610         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
23611         
23612         var iw = this.align == 'center' ? this.width : '100%';
23613         var img =   {
23614             tag : 'img',
23615             contenteditable : 'false',
23616             src : this.image_src,
23617             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23618             style: {
23619                 width : iw,
23620                 maxWidth : iw + ' !important', // this is not getting rendered?
23621                 margin : m  
23622                 
23623             }
23624         };
23625         /*
23626         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23627                     '<a href="{2}">' + 
23628                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
23629                     '</a>' + 
23630                 '</div>',
23631         */
23632                 
23633         if (this.href.length > 0) {
23634             img = {
23635                 tag : 'a',
23636                 href: this.href,
23637                 contenteditable : 'true',
23638                 cn : [
23639                     img
23640                 ]
23641             };
23642         }
23643         
23644         
23645         if (this.video_url.length > 0) {
23646             img = {
23647                 tag : 'div',
23648                 cls : this.cls,
23649                 frameborder : 0,
23650                 allowfullscreen : true,
23651                 width : 420,  // these are for video tricks - that we replace the outer
23652                 height : 315,
23653                 src : this.video_url,
23654                 cn : [
23655                     img
23656                 ]
23657             };
23658         }
23659         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
23660         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
23661         
23662   
23663         var ret =   {
23664             tag: 'figure',
23665             'data-block' : 'Figure',
23666             
23667             contenteditable : 'false',
23668             
23669             style : {
23670                 display: 'block',
23671                 float :  this.align ,
23672                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
23673                 width : this.align == 'center' ? '100%' : this.width,
23674                 margin:  '0px',
23675                 padding: this.align == 'center' ? '0' : '0 10px' ,
23676                 textAlign : this.align   // seems to work for email..
23677                 
23678             },
23679            
23680             
23681             align : this.align,
23682             cn : [
23683                 img,
23684               
23685                 {
23686                     tag: 'figcaption',
23687                     'data-display' : this.caption_display,
23688                     style : {
23689                         textAlign : 'left',
23690                         fontSize : '16px',
23691                         lineHeight : '24px',
23692                         display : this.caption_display,
23693                         maxWidth : this.width + ' !important',
23694                         margin: m,
23695                         width: this.width
23696                     
23697                          
23698                     },
23699                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
23700                     cn : [
23701                         {
23702                             tag: 'div',
23703                             style  : {
23704                                 marginTop : '16px',
23705                                 textAlign : 'left'
23706                             },
23707                             align: 'left',
23708                             cn : [
23709                                 {
23710                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
23711                                     tag : 'i',
23712                                     contenteditable : true,
23713                                     html : captionhtml
23714                                 }
23715                                 
23716                             ]
23717                         }
23718                         
23719                     ]
23720                     
23721                 }
23722             ]
23723         };
23724         return ret;
23725          
23726     },
23727     
23728     readElement : function(node)
23729     {
23730         // this should not really come from the link...
23731         this.video_url = this.getVal(node, 'div', 'src');
23732         this.cls = this.getVal(node, 'div', 'class');
23733         this.href = this.getVal(node, 'a', 'href');
23734         
23735         
23736         this.image_src = this.getVal(node, 'img', 'src');
23737          
23738         this.align = this.getVal(node, 'figure', 'align');
23739         var figcaption = this.getVal(node, 'figcaption', false);
23740         this.caption = this.getVal(figcaption, 'i', 'html');
23741
23742         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
23743         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23744         this.width = this.getVal(node, 'figcaption', 'style', 'width');
23745         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23746         
23747     },
23748     removeNode : function()
23749     {
23750         return this.node;
23751     }
23752     
23753   
23754    
23755      
23756     
23757     
23758     
23759     
23760 })
23761
23762  
23763
23764 /**
23765  * @class Roo.htmleditor.BlockTable
23766  * Block that manages a table
23767  * 
23768  * @constructor
23769  * Create a new Filter.
23770  * @param {Object} config Configuration options
23771  */
23772
23773 Roo.htmleditor.BlockTable = function(cfg)
23774 {
23775     if (cfg.node) {
23776         this.readElement(cfg.node);
23777         this.updateElement(cfg.node);
23778     }
23779     Roo.apply(this, cfg);
23780     if (!cfg.node) {
23781         this.rows = [];
23782         for(var r = 0; r < this.no_row; r++) {
23783             this.rows[r] = [];
23784             for(var c = 0; c < this.no_col; c++) {
23785                 this.rows[r][c] = this.emptyCell();
23786             }
23787         }
23788     }
23789     
23790     
23791 }
23792 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23793  
23794     rows : false,
23795     no_col : 1,
23796     no_row : 1,
23797     
23798     
23799     width: '100%',
23800     
23801     // used by context menu
23802     friendly_name : 'Table',
23803     deleteTitle : 'Delete Table',
23804     // context menu is drawn once..
23805     
23806     contextMenu : function(toolbar)
23807     {
23808         
23809         var block = function() {
23810             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23811         };
23812         
23813         
23814         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23815         
23816         var syncValue = toolbar.editorcore.syncValue;
23817         
23818         var fields = {};
23819         
23820         return [
23821             {
23822                 xtype : 'TextItem',
23823                 text : "Width: ",
23824                 xns : rooui.Toolbar  //Boostrap?
23825             },
23826             {
23827                 xtype : 'ComboBox',
23828                 allowBlank : false,
23829                 displayField : 'val',
23830                 editable : true,
23831                 listWidth : 100,
23832                 triggerAction : 'all',
23833                 typeAhead : true,
23834                 valueField : 'val',
23835                 width : 100,
23836                 name : 'width',
23837                 listeners : {
23838                     select : function (combo, r, index)
23839                     {
23840                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23841                         var b = block();
23842                         b.width = r.get('val');
23843                         b.updateElement();
23844                         syncValue();
23845                         toolbar.editorcore.onEditorEvent();
23846                     }
23847                 },
23848                 xns : rooui.form,
23849                 store : {
23850                     xtype : 'SimpleStore',
23851                     data : [
23852                         ['100%'],
23853                         ['auto']
23854                     ],
23855                     fields : [ 'val'],
23856                     xns : Roo.data
23857                 }
23858             },
23859             // -------- Cols
23860             
23861             {
23862                 xtype : 'TextItem',
23863                 text : "Columns: ",
23864                 xns : rooui.Toolbar  //Boostrap?
23865             },
23866          
23867             {
23868                 xtype : 'Button',
23869                 text: '-',
23870                 listeners : {
23871                     click : function (_self, e)
23872                     {
23873                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23874                         block().removeColumn();
23875                         syncValue();
23876                         toolbar.editorcore.onEditorEvent();
23877                     }
23878                 },
23879                 xns : rooui.Toolbar
23880             },
23881             {
23882                 xtype : 'Button',
23883                 text: '+',
23884                 listeners : {
23885                     click : function (_self, e)
23886                     {
23887                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23888                         block().addColumn();
23889                         syncValue();
23890                         toolbar.editorcore.onEditorEvent();
23891                     }
23892                 },
23893                 xns : rooui.Toolbar
23894             },
23895             // -------- ROWS
23896             {
23897                 xtype : 'TextItem',
23898                 text : "Rows: ",
23899                 xns : rooui.Toolbar  //Boostrap?
23900             },
23901          
23902             {
23903                 xtype : 'Button',
23904                 text: '-',
23905                 listeners : {
23906                     click : function (_self, e)
23907                     {
23908                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23909                         block().removeRow();
23910                         syncValue();
23911                         toolbar.editorcore.onEditorEvent();
23912                     }
23913                 },
23914                 xns : rooui.Toolbar
23915             },
23916             {
23917                 xtype : 'Button',
23918                 text: '+',
23919                 listeners : {
23920                     click : function (_self, e)
23921                     {
23922                         block().addRow();
23923                         syncValue();
23924                         toolbar.editorcore.onEditorEvent();
23925                     }
23926                 },
23927                 xns : rooui.Toolbar
23928             },
23929             // -------- ROWS
23930             {
23931                 xtype : 'Button',
23932                 text: 'Reset Column Widths',
23933                 listeners : {
23934                     
23935                     click : function (_self, e)
23936                     {
23937                         block().resetWidths();
23938                         syncValue();
23939                         toolbar.editorcore.onEditorEvent();
23940                     }
23941                 },
23942                 xns : rooui.Toolbar
23943             } 
23944             
23945             
23946             
23947         ];
23948         
23949     },
23950     
23951     
23952   /**
23953      * create a DomHelper friendly object - for use with
23954      * Roo.DomHelper.markup / overwrite / etc..
23955      * ?? should it be called with option to hide all editing features?
23956      */
23957     toObject : function()
23958     {
23959         
23960         var ret = {
23961             tag : 'table',
23962             contenteditable : 'false', // this stops cell selection from picking the table.
23963             'data-block' : 'Table',
23964             style : {
23965                 width:  this.width,
23966                 border : 'solid 1px #000', // ??? hard coded?
23967                 'border-collapse' : 'collapse' 
23968             },
23969             cn : [
23970                 { tag : 'tbody' , cn : [] }
23971             ]
23972         };
23973         
23974         // do we have a head = not really 
23975         var ncols = 0;
23976         Roo.each(this.rows, function( row ) {
23977             var tr = {
23978                 tag: 'tr',
23979                 style : {
23980                     margin: '6px',
23981                     border : 'solid 1px #000',
23982                     textAlign : 'left' 
23983                 },
23984                 cn : [ ]
23985             };
23986             
23987             ret.cn[0].cn.push(tr);
23988             // does the row have any properties? ?? height?
23989             var nc = 0;
23990             Roo.each(row, function( cell ) {
23991                 
23992                 var td = {
23993                     tag : 'td',
23994                     contenteditable :  'true',
23995                     'data-block' : 'Td',
23996                     html : cell.html,
23997                     style : cell.style
23998                 };
23999                 if (cell.colspan > 1) {
24000                     td.colspan = cell.colspan ;
24001                     nc += cell.colspan;
24002                 } else {
24003                     nc++;
24004                 }
24005                 if (cell.rowspan > 1) {
24006                     td.rowspan = cell.rowspan ;
24007                 }
24008                 
24009                 
24010                 // widths ?
24011                 tr.cn.push(td);
24012                     
24013                 
24014             }, this);
24015             ncols = Math.max(nc, ncols);
24016             
24017             
24018         }, this);
24019         // add the header row..
24020         
24021         ncols++;
24022          
24023         
24024         return ret;
24025          
24026     },
24027     
24028     readElement : function(node)
24029     {
24030         node  = node ? node : this.node ;
24031         this.width = this.getVal(node, true, 'style', 'width') || '100%';
24032         
24033         this.rows = [];
24034         this.no_row = 0;
24035         var trs = Array.from(node.rows);
24036         trs.forEach(function(tr) {
24037             var row =  [];
24038             this.rows.push(row);
24039             
24040             this.no_row++;
24041             var no_column = 0;
24042             Array.from(tr.cells).forEach(function(td) {
24043                 
24044                 var add = {
24045                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24046                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24047                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24048                     html : td.innerHTML
24049                 };
24050                 no_column += add.colspan;
24051                      
24052                 
24053                 row.push(add);
24054                 
24055                 
24056             },this);
24057             this.no_col = Math.max(this.no_col, no_column);
24058             
24059             
24060         },this);
24061         
24062         
24063     },
24064     normalizeRows: function()
24065     {
24066         var ret= [];
24067         var rid = -1;
24068         this.rows.forEach(function(row) {
24069             rid++;
24070             ret[rid] = [];
24071             row = this.normalizeRow(row);
24072             var cid = 0;
24073             row.forEach(function(c) {
24074                 while (typeof(ret[rid][cid]) != 'undefined') {
24075                     cid++;
24076                 }
24077                 if (typeof(ret[rid]) == 'undefined') {
24078                     ret[rid] = [];
24079                 }
24080                 ret[rid][cid] = c;
24081                 c.row = rid;
24082                 c.col = cid;
24083                 if (c.rowspan < 2) {
24084                     return;
24085                 }
24086                 
24087                 for(var i = 1 ;i < c.rowspan; i++) {
24088                     if (typeof(ret[rid+i]) == 'undefined') {
24089                         ret[rid+i] = [];
24090                     }
24091                     ret[rid+i][cid] = c;
24092                 }
24093             });
24094         }, this);
24095         return ret;
24096     
24097     },
24098     
24099     normalizeRow: function(row)
24100     {
24101         var ret= [];
24102         row.forEach(function(c) {
24103             if (c.colspan < 2) {
24104                 ret.push(c);
24105                 return;
24106             }
24107             for(var i =0 ;i < c.colspan; i++) {
24108                 ret.push(c);
24109             }
24110         });
24111         return ret;
24112     
24113     },
24114     
24115     deleteColumn : function(sel)
24116     {
24117         if (!sel || sel.type != 'col') {
24118             return;
24119         }
24120         if (this.no_col < 2) {
24121             return;
24122         }
24123         
24124         this.rows.forEach(function(row) {
24125             var cols = this.normalizeRow(row);
24126             var col = cols[sel.col];
24127             if (col.colspan > 1) {
24128                 col.colspan --;
24129             } else {
24130                 row.remove(col);
24131             }
24132             
24133         }, this);
24134         this.no_col--;
24135         
24136     },
24137     removeColumn : function()
24138     {
24139         this.deleteColumn({
24140             type: 'col',
24141             col : this.no_col-1
24142         });
24143         this.updateElement();
24144     },
24145     
24146      
24147     addColumn : function()
24148     {
24149         
24150         this.rows.forEach(function(row) {
24151             row.push(this.emptyCell());
24152            
24153         }, this);
24154         this.updateElement();
24155     },
24156     
24157     deleteRow : function(sel)
24158     {
24159         if (!sel || sel.type != 'row') {
24160             return;
24161         }
24162         
24163         if (this.no_row < 2) {
24164             return;
24165         }
24166         
24167         var rows = this.normalizeRows();
24168         
24169         
24170         rows[sel.row].forEach(function(col) {
24171             if (col.rowspan > 1) {
24172                 col.rowspan--;
24173             } else {
24174                 col.remove = 1; // flage it as removed.
24175             }
24176             
24177         }, this);
24178         var newrows = [];
24179         this.rows.forEach(function(row) {
24180             newrow = [];
24181             row.forEach(function(c) {
24182                 if (typeof(c.remove) == 'undefined') {
24183                     newrow.push(c);
24184                 }
24185                 
24186             });
24187             if (newrow.length > 0) {
24188                 newrows.push(row);
24189             }
24190         });
24191         this.rows =  newrows;
24192         
24193         
24194         
24195         this.no_row--;
24196         this.updateElement();
24197         
24198     },
24199     removeRow : function()
24200     {
24201         this.deleteRow({
24202             type: 'row',
24203             row : this.no_row-1
24204         });
24205         
24206     },
24207     
24208      
24209     addRow : function()
24210     {
24211         
24212         var row = [];
24213         for (var i = 0; i < this.no_col; i++ ) {
24214             
24215             row.push(this.emptyCell());
24216            
24217         }
24218         this.rows.push(row);
24219         this.updateElement();
24220         
24221     },
24222      
24223     // the default cell object... at present...
24224     emptyCell : function() {
24225         return (new Roo.htmleditor.BlockTd({})).toObject();
24226         
24227      
24228     },
24229     
24230     removeNode : function()
24231     {
24232         return this.node;
24233     },
24234     
24235     
24236     
24237     resetWidths : function()
24238     {
24239         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24240             var nn = Roo.htmleditor.Block.factory(n);
24241             nn.width = '';
24242             nn.updateElement(n);
24243         });
24244     }
24245     
24246     
24247     
24248     
24249 })
24250
24251 /**
24252  *
24253  * editing a TD?
24254  *
24255  * since selections really work on the table cell, then editing really should work from there
24256  *
24257  * The original plan was to support merging etc... - but that may not be needed yet..
24258  *
24259  * So this simple version will support:
24260  *   add/remove cols
24261  *   adjust the width +/-
24262  *   reset the width...
24263  *   
24264  *
24265  */
24266
24267
24268  
24269
24270 /**
24271  * @class Roo.htmleditor.BlockTable
24272  * Block that manages a table
24273  * 
24274  * @constructor
24275  * Create a new Filter.
24276  * @param {Object} config Configuration options
24277  */
24278
24279 Roo.htmleditor.BlockTd = function(cfg)
24280 {
24281     if (cfg.node) {
24282         this.readElement(cfg.node);
24283         this.updateElement(cfg.node);
24284     }
24285     Roo.apply(this, cfg);
24286      
24287     
24288     
24289 }
24290 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24291  
24292     node : false,
24293     
24294     width: '',
24295     textAlign : 'left',
24296     valign : 'top',
24297     
24298     colspan : 1,
24299     rowspan : 1,
24300     
24301     
24302     // used by context menu
24303     friendly_name : 'Table Cell',
24304     deleteTitle : false, // use our customer delete
24305     
24306     // context menu is drawn once..
24307     
24308     contextMenu : function(toolbar)
24309     {
24310         
24311         var cell = function() {
24312             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24313         };
24314         
24315         var table = function() {
24316             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24317         };
24318         
24319         var lr = false;
24320         var saveSel = function()
24321         {
24322             lr = toolbar.editorcore.getSelection().getRangeAt(0);
24323         }
24324         var restoreSel = function()
24325         {
24326             if (lr) {
24327                 (function() {
24328                     toolbar.editorcore.focus();
24329                     var cr = toolbar.editorcore.getSelection();
24330                     cr.removeAllRanges();
24331                     cr.addRange(lr);
24332                     toolbar.editorcore.onEditorEvent();
24333                 }).defer(10, this);
24334                 
24335                 
24336             }
24337         }
24338         
24339         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24340         
24341         var syncValue = toolbar.editorcore.syncValue;
24342         
24343         var fields = {};
24344         
24345         return [
24346             {
24347                 xtype : 'Button',
24348                 text : 'Edit Table',
24349                 listeners : {
24350                     click : function() {
24351                         var t = toolbar.tb.selectedNode.closest('table');
24352                         toolbar.editorcore.selectNode(t);
24353                         toolbar.editorcore.onEditorEvent();                        
24354                     }
24355                 }
24356                 
24357             },
24358               
24359            
24360              
24361             {
24362                 xtype : 'TextItem',
24363                 text : "Column Width: ",
24364                  xns : rooui.Toolbar 
24365                
24366             },
24367             {
24368                 xtype : 'Button',
24369                 text: '-',
24370                 listeners : {
24371                     click : function (_self, e)
24372                     {
24373                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24374                         cell().shrinkColumn();
24375                         syncValue();
24376                          toolbar.editorcore.onEditorEvent();
24377                     }
24378                 },
24379                 xns : rooui.Toolbar
24380             },
24381             {
24382                 xtype : 'Button',
24383                 text: '+',
24384                 listeners : {
24385                     click : function (_self, e)
24386                     {
24387                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24388                         cell().growColumn();
24389                         syncValue();
24390                         toolbar.editorcore.onEditorEvent();
24391                     }
24392                 },
24393                 xns : rooui.Toolbar
24394             },
24395             
24396             {
24397                 xtype : 'TextItem',
24398                 text : "Vertical Align: ",
24399                 xns : rooui.Toolbar  //Boostrap?
24400             },
24401             {
24402                 xtype : 'ComboBox',
24403                 allowBlank : false,
24404                 displayField : 'val',
24405                 editable : true,
24406                 listWidth : 100,
24407                 triggerAction : 'all',
24408                 typeAhead : true,
24409                 valueField : 'val',
24410                 width : 100,
24411                 name : 'valign',
24412                 listeners : {
24413                     select : function (combo, r, index)
24414                     {
24415                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24416                         var b = cell();
24417                         b.valign = r.get('val');
24418                         b.updateElement();
24419                         syncValue();
24420                         toolbar.editorcore.onEditorEvent();
24421                     }
24422                 },
24423                 xns : rooui.form,
24424                 store : {
24425                     xtype : 'SimpleStore',
24426                     data : [
24427                         ['top'],
24428                         ['middle'],
24429                         ['bottom'] // there are afew more... 
24430                     ],
24431                     fields : [ 'val'],
24432                     xns : Roo.data
24433                 }
24434             },
24435             
24436             {
24437                 xtype : 'TextItem',
24438                 text : "Merge Cells: ",
24439                  xns : rooui.Toolbar 
24440                
24441             },
24442             
24443             
24444             {
24445                 xtype : 'Button',
24446                 text: 'Right',
24447                 listeners : {
24448                     click : function (_self, e)
24449                     {
24450                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24451                         cell().mergeRight();
24452                         //block().growColumn();
24453                         syncValue();
24454                         toolbar.editorcore.onEditorEvent();
24455                     }
24456                 },
24457                 xns : rooui.Toolbar
24458             },
24459              
24460             {
24461                 xtype : 'Button',
24462                 text: 'Below',
24463                 listeners : {
24464                     click : function (_self, e)
24465                     {
24466                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24467                         cell().mergeBelow();
24468                         //block().growColumn();
24469                         syncValue();
24470                         toolbar.editorcore.onEditorEvent();
24471                     }
24472                 },
24473                 xns : rooui.Toolbar
24474             },
24475             {
24476                 xtype : 'TextItem',
24477                 text : "| ",
24478                  xns : rooui.Toolbar 
24479                
24480             },
24481             
24482             {
24483                 xtype : 'Button',
24484                 text: 'Split',
24485                 listeners : {
24486                     click : function (_self, e)
24487                     {
24488                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24489                         cell().split();
24490                         syncValue();
24491                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24492                         toolbar.editorcore.onEditorEvent();
24493                                              
24494                     }
24495                 },
24496                 xns : rooui.Toolbar
24497             },
24498             {
24499                 xtype : 'Fill',
24500                 xns : rooui.Toolbar 
24501                
24502             },
24503         
24504           
24505             {
24506                 xtype : 'Button',
24507                 text: 'Delete',
24508                  
24509                 xns : rooui.Toolbar,
24510                 menu : {
24511                     xtype : 'Menu',
24512                     xns : rooui.menu,
24513                     items : [
24514                         {
24515                             xtype : 'Item',
24516                             html: 'Column',
24517                             listeners : {
24518                                 click : function (_self, e)
24519                                 {
24520                                     var t = table();
24521                                     
24522                                     cell().deleteColumn();
24523                                     syncValue();
24524                                     toolbar.editorcore.selectNode(t.node);
24525                                     toolbar.editorcore.onEditorEvent();   
24526                                 }
24527                             },
24528                             xns : rooui.menu
24529                         },
24530                         {
24531                             xtype : 'Item',
24532                             html: 'Row',
24533                             listeners : {
24534                                 click : function (_self, e)
24535                                 {
24536                                     var t = table();
24537                                     cell().deleteRow();
24538                                     syncValue();
24539                                     
24540                                     toolbar.editorcore.selectNode(t.node);
24541                                     toolbar.editorcore.onEditorEvent();   
24542                                                          
24543                                 }
24544                             },
24545                             xns : rooui.menu
24546                         },
24547                        {
24548                             xtype : 'Separator',
24549                             xns : rooui.menu
24550                         },
24551                         {
24552                             xtype : 'Item',
24553                             html: 'Table',
24554                             listeners : {
24555                                 click : function (_self, e)
24556                                 {
24557                                     var t = table();
24558                                     var nn = t.node.nextSibling || t.node.previousSibling;
24559                                     t.node.parentNode.removeChild(t.node);
24560                                     if (nn) { 
24561                                         toolbar.editorcore.selectNode(nn, true);
24562                                     }
24563                                     toolbar.editorcore.onEditorEvent();   
24564                                                          
24565                                 }
24566                             },
24567                             xns : rooui.menu
24568                         }
24569                     ]
24570                 }
24571             }
24572             
24573             // align... << fixme
24574             
24575         ];
24576         
24577     },
24578     
24579     
24580   /**
24581      * create a DomHelper friendly object - for use with
24582      * Roo.DomHelper.markup / overwrite / etc..
24583      * ?? should it be called with option to hide all editing features?
24584      */
24585  /**
24586      * create a DomHelper friendly object - for use with
24587      * Roo.DomHelper.markup / overwrite / etc..
24588      * ?? should it be called with option to hide all editing features?
24589      */
24590     toObject : function()
24591     {
24592         
24593         var ret = {
24594             tag : 'td',
24595             contenteditable : 'true', // this stops cell selection from picking the table.
24596             'data-block' : 'Td',
24597             valign : this.valign,
24598             style : {  
24599                 'text-align' :  this.textAlign,
24600                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24601                 'border-collapse' : 'collapse',
24602                 padding : '6px', // 8 for desktop / 4 for mobile
24603                 'vertical-align': this.valign
24604             },
24605             html : this.html
24606         };
24607         if (this.width != '') {
24608             ret.width = this.width;
24609             ret.style.width = this.width;
24610         }
24611         
24612         
24613         if (this.colspan > 1) {
24614             ret.colspan = this.colspan ;
24615         } 
24616         if (this.rowspan > 1) {
24617             ret.rowspan = this.rowspan ;
24618         }
24619         
24620            
24621         
24622         return ret;
24623          
24624     },
24625     
24626     readElement : function(node)
24627     {
24628         node  = node ? node : this.node ;
24629         this.width = node.style.width;
24630         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24631         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24632         this.html = node.innerHTML;
24633         
24634         
24635     },
24636      
24637     // the default cell object... at present...
24638     emptyCell : function() {
24639         return {
24640             colspan :  1,
24641             rowspan :  1,
24642             textAlign : 'left',
24643             html : "&nbsp;" // is this going to be editable now?
24644         };
24645      
24646     },
24647     
24648     removeNode : function()
24649     {
24650         return this.node.closest('table');
24651          
24652     },
24653     
24654     cellData : false,
24655     
24656     colWidths : false,
24657     
24658     toTableArray  : function()
24659     {
24660         var ret = [];
24661         var tab = this.node.closest('tr').closest('table');
24662         Array.from(tab.rows).forEach(function(r, ri){
24663             ret[ri] = [];
24664         });
24665         var rn = 0;
24666         this.colWidths = [];
24667         var all_auto = true;
24668         Array.from(tab.rows).forEach(function(r, ri){
24669             
24670             var cn = 0;
24671             Array.from(r.cells).forEach(function(ce, ci){
24672                 var c =  {
24673                     cell : ce,
24674                     row : rn,
24675                     col: cn,
24676                     colspan : ce.colSpan,
24677                     rowspan : ce.rowSpan
24678                 };
24679                 if (ce.isEqualNode(this.node)) {
24680                     this.cellData = c;
24681                 }
24682                 // if we have been filled up by a row?
24683                 if (typeof(ret[rn][cn]) != 'undefined') {
24684                     while(typeof(ret[rn][cn]) != 'undefined') {
24685                         cn++;
24686                     }
24687                     c.col = cn;
24688                 }
24689                 
24690                 if (typeof(this.colWidths[cn]) == 'undefined') {
24691                     this.colWidths[cn] =   ce.style.width;
24692                     if (this.colWidths[cn] != '') {
24693                         all_auto = false;
24694                     }
24695                 }
24696                 
24697                 
24698                 if (c.colspan < 2 && c.rowspan < 2 ) {
24699                     ret[rn][cn] = c;
24700                     cn++;
24701                     return;
24702                 }
24703                 for(var j = 0; j < c.rowspan; j++) {
24704                     if (typeof(ret[rn+j]) == 'undefined') {
24705                         continue; // we have a problem..
24706                     }
24707                     ret[rn+j][cn] = c;
24708                     for(var i = 0; i < c.colspan; i++) {
24709                         ret[rn+j][cn+i] = c;
24710                     }
24711                 }
24712                 
24713                 cn += c.colspan;
24714             }, this);
24715             rn++;
24716         }, this);
24717         
24718         // initalize widths.?
24719         // either all widths or no widths..
24720         if (all_auto) {
24721             this.colWidths[0] = false; // no widths flag.
24722         }
24723         
24724         
24725         return ret;
24726         
24727     },
24728     
24729     
24730     
24731     
24732     mergeRight: function()
24733     {
24734          
24735         // get the contents of the next cell along..
24736         var tr = this.node.closest('tr');
24737         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24738         if (i >= tr.childNodes.length - 1) {
24739             return; // no cells on right to merge with.
24740         }
24741         var table = this.toTableArray();
24742         
24743         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24744             return; // nothing right?
24745         }
24746         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24747         // right cell - must be same rowspan and on the same row.
24748         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24749             return; // right hand side is not same rowspan.
24750         }
24751         
24752         
24753         
24754         this.node.innerHTML += ' ' + rc.cell.innerHTML;
24755         tr.removeChild(rc.cell);
24756         this.colspan += rc.colspan;
24757         this.node.setAttribute('colspan', this.colspan);
24758
24759     },
24760     
24761     
24762     mergeBelow : function()
24763     {
24764         var table = this.toTableArray();
24765         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24766             return; // no row below
24767         }
24768         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24769             return; // nothing right?
24770         }
24771         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24772         
24773         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24774             return; // right hand side is not same rowspan.
24775         }
24776         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
24777         rc.cell.parentNode.removeChild(rc.cell);
24778         this.rowspan += rc.rowspan;
24779         this.node.setAttribute('rowspan', this.rowspan);
24780     },
24781     
24782     split: function()
24783     {
24784         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24785             return;
24786         }
24787         var table = this.toTableArray();
24788         var cd = this.cellData;
24789         this.rowspan = 1;
24790         this.colspan = 1;
24791         
24792         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24793             
24794             
24795             
24796             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24797                 if (r == cd.row && c == cd.col) {
24798                     this.node.removeAttribute('rowspan');
24799                     this.node.removeAttribute('colspan');
24800                     continue;
24801                 }
24802                  
24803                 var ntd = this.node.cloneNode(); // which col/row should be 0..
24804                 ntd.removeAttribute('id'); //
24805                 //ntd.style.width  = '';
24806                 ntd.innerHTML = '';
24807                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
24808             }
24809             
24810         }
24811         this.redrawAllCells(table);
24812         
24813          
24814         
24815     },
24816     
24817     
24818     
24819     redrawAllCells: function(table)
24820     {
24821         
24822          
24823         var tab = this.node.closest('tr').closest('table');
24824         var ctr = tab.rows[0].parentNode;
24825         Array.from(tab.rows).forEach(function(r, ri){
24826             
24827             Array.from(r.cells).forEach(function(ce, ci){
24828                 ce.parentNode.removeChild(ce);
24829             });
24830             r.parentNode.removeChild(r);
24831         });
24832         for(var r = 0 ; r < table.length; r++) {
24833             var re = tab.rows[r];
24834             
24835             var re = tab.ownerDocument.createElement('tr');
24836             ctr.appendChild(re);
24837             for(var c = 0 ; c < table[r].length; c++) {
24838                 if (table[r][c].cell === false) {
24839                     continue;
24840                 }
24841                 
24842                 re.appendChild(table[r][c].cell);
24843                  
24844                 table[r][c].cell = false;
24845             }
24846         }
24847         
24848     },
24849     updateWidths : function(table)
24850     {
24851         for(var r = 0 ; r < table.length; r++) {
24852            
24853             for(var c = 0 ; c < table[r].length; c++) {
24854                 if (table[r][c].cell === false) {
24855                     continue;
24856                 }
24857                 
24858                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
24859                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
24860                     el.width = Math.floor(this.colWidths[c])  +'%';
24861                     el.updateElement(el.node);
24862                 }
24863                 table[r][c].cell = false; // done
24864             }
24865         }
24866     },
24867     normalizeWidths : function(table)
24868     {
24869     
24870         if (this.colWidths[0] === false) {
24871             var nw = 100.0 / this.colWidths.length;
24872             this.colWidths.forEach(function(w,i) {
24873                 this.colWidths[i] = nw;
24874             },this);
24875             return;
24876         }
24877     
24878         var t = 0, missing = [];
24879         
24880         this.colWidths.forEach(function(w,i) {
24881             //if you mix % and
24882             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
24883             var add =  this.colWidths[i];
24884             if (add > 0) {
24885                 t+=add;
24886                 return;
24887             }
24888             missing.push(i);
24889             
24890             
24891         },this);
24892         var nc = this.colWidths.length;
24893         if (missing.length) {
24894             var mult = (nc - missing.length) / (1.0 * nc);
24895             var t = mult * t;
24896             var ew = (100 -t) / (1.0 * missing.length);
24897             this.colWidths.forEach(function(w,i) {
24898                 if (w > 0) {
24899                     this.colWidths[i] = w * mult;
24900                     return;
24901                 }
24902                 
24903                 this.colWidths[i] = ew;
24904             }, this);
24905             // have to make up numbers..
24906              
24907         }
24908         // now we should have all the widths..
24909         
24910     
24911     },
24912     
24913     shrinkColumn : function()
24914     {
24915         var table = this.toTableArray();
24916         this.normalizeWidths(table);
24917         var col = this.cellData.col;
24918         var nw = this.colWidths[col] * 0.8;
24919         if (nw < 5) {
24920             return;
24921         }
24922         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24923         this.colWidths.forEach(function(w,i) {
24924             if (i == col) {
24925                  this.colWidths[i] = nw;
24926                 return;
24927             }
24928             this.colWidths[i] += otherAdd
24929         }, this);
24930         this.updateWidths(table);
24931          
24932     },
24933     growColumn : function()
24934     {
24935         var table = this.toTableArray();
24936         this.normalizeWidths(table);
24937         var col = this.cellData.col;
24938         var nw = this.colWidths[col] * 1.2;
24939         if (nw > 90) {
24940             return;
24941         }
24942         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
24943         this.colWidths.forEach(function(w,i) {
24944             if (i == col) {
24945                 this.colWidths[i] = nw;
24946                 return;
24947             }
24948             this.colWidths[i] -= otherSub
24949         }, this);
24950         this.updateWidths(table);
24951          
24952     },
24953     deleteRow : function()
24954     {
24955         // delete this rows 'tr'
24956         // if any of the cells in this row have a rowspan > 1 && row!= this row..
24957         // then reduce the rowspan.
24958         var table = this.toTableArray();
24959         // this.cellData.row;
24960         for (var i =0;i< table[this.cellData.row].length ; i++) {
24961             var c = table[this.cellData.row][i];
24962             if (c.row != this.cellData.row) {
24963                 
24964                 c.rowspan--;
24965                 c.cell.setAttribute('rowspan', c.rowspan);
24966                 continue;
24967             }
24968             if (c.rowspan > 1) {
24969                 c.rowspan--;
24970                 c.cell.setAttribute('rowspan', c.rowspan);
24971             }
24972         }
24973         table.splice(this.cellData.row,1);
24974         this.redrawAllCells(table);
24975         
24976     },
24977     deleteColumn : function()
24978     {
24979         var table = this.toTableArray();
24980         
24981         for (var i =0;i< table.length ; i++) {
24982             var c = table[i][this.cellData.col];
24983             if (c.col != this.cellData.col) {
24984                 table[i][this.cellData.col].colspan--;
24985             } else if (c.colspan > 1) {
24986                 c.colspan--;
24987                 c.cell.setAttribute('colspan', c.colspan);
24988             }
24989             table[i].splice(this.cellData.col,1);
24990         }
24991         
24992         this.redrawAllCells(table);
24993     }
24994     
24995     
24996     
24997     
24998 })
24999
25000 //<script type="text/javascript">
25001
25002 /*
25003  * Based  Ext JS Library 1.1.1
25004  * Copyright(c) 2006-2007, Ext JS, LLC.
25005  * LGPL
25006  *
25007  */
25008  
25009 /**
25010  * @class Roo.HtmlEditorCore
25011  * @extends Roo.Component
25012  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25013  *
25014  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25015  */
25016
25017 Roo.HtmlEditorCore = function(config){
25018     
25019     
25020     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25021     
25022     
25023     this.addEvents({
25024         /**
25025          * @event initialize
25026          * Fires when the editor is fully initialized (including the iframe)
25027          * @param {Roo.HtmlEditorCore} this
25028          */
25029         initialize: true,
25030         /**
25031          * @event activate
25032          * Fires when the editor is first receives the focus. Any insertion must wait
25033          * until after this event.
25034          * @param {Roo.HtmlEditorCore} this
25035          */
25036         activate: true,
25037          /**
25038          * @event beforesync
25039          * Fires before the textarea is updated with content from the editor iframe. Return false
25040          * to cancel the sync.
25041          * @param {Roo.HtmlEditorCore} this
25042          * @param {String} html
25043          */
25044         beforesync: true,
25045          /**
25046          * @event beforepush
25047          * Fires before the iframe editor is updated with content from the textarea. Return false
25048          * to cancel the push.
25049          * @param {Roo.HtmlEditorCore} this
25050          * @param {String} html
25051          */
25052         beforepush: true,
25053          /**
25054          * @event sync
25055          * Fires when the textarea is updated with content from the editor iframe.
25056          * @param {Roo.HtmlEditorCore} this
25057          * @param {String} html
25058          */
25059         sync: true,
25060          /**
25061          * @event push
25062          * Fires when the iframe editor is updated with content from the textarea.
25063          * @param {Roo.HtmlEditorCore} this
25064          * @param {String} html
25065          */
25066         push: true,
25067         
25068         /**
25069          * @event editorevent
25070          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25071          * @param {Roo.HtmlEditorCore} this
25072          */
25073         editorevent: true 
25074          
25075         
25076     });
25077     
25078     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25079     
25080     // defaults : white / black...
25081     this.applyBlacklists();
25082     
25083     
25084     
25085 };
25086
25087
25088 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25089
25090
25091      /**
25092      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25093      */
25094     
25095     owner : false,
25096     
25097      /**
25098      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25099      *                        Roo.resizable.
25100      */
25101     resizable : false,
25102      /**
25103      * @cfg {Number} height (in pixels)
25104      */   
25105     height: 300,
25106    /**
25107      * @cfg {Number} width (in pixels)
25108      */   
25109     width: 500,
25110      /**
25111      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25112      *         if you are doing an email editor, this probably needs disabling, it's designed
25113      */
25114     autoClean: true,
25115     
25116     /**
25117      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25118      */
25119     enableBlocks : true,
25120     /**
25121      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25122      * 
25123      */
25124     stylesheets: false,
25125      /**
25126      * @cfg {String} language default en - language of text (usefull for rtl languages)
25127      * 
25128      */
25129     language: 'en',
25130     
25131     /**
25132      * @cfg {boolean} allowComments - default false - allow comments in HTML source
25133      *          - by default they are stripped - if you are editing email you may need this.
25134      */
25135     allowComments: false,
25136     // id of frame..
25137     frameId: false,
25138     
25139     // private properties
25140     validationEvent : false,
25141     deferHeight: true,
25142     initialized : false,
25143     activated : false,
25144     sourceEditMode : false,
25145     onFocus : Roo.emptyFn,
25146     iframePad:3,
25147     hideMode:'offsets',
25148     
25149     clearUp: true,
25150     
25151     // blacklist + whitelisted elements..
25152     black: false,
25153     white: false,
25154      
25155     bodyCls : '',
25156
25157     
25158     undoManager : false,
25159     /**
25160      * Protected method that will not generally be called directly. It
25161      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25162      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25163      */
25164     getDocMarkup : function(){
25165         // body styles..
25166         var st = '';
25167         
25168         // inherit styels from page...?? 
25169         if (this.stylesheets === false) {
25170             
25171             Roo.get(document.head).select('style').each(function(node) {
25172                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25173             });
25174             
25175             Roo.get(document.head).select('link').each(function(node) { 
25176                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25177             });
25178             
25179         } else if (!this.stylesheets.length) {
25180                 // simple..
25181                 st = '<style type="text/css">' +
25182                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25183                    '</style>';
25184         } else {
25185             for (var i in this.stylesheets) {
25186                 if (typeof(this.stylesheets[i]) != 'string') {
25187                     continue;
25188                 }
25189                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25190             }
25191             
25192         }
25193         
25194         st +=  '<style type="text/css">' +
25195             'IMG { cursor: pointer } ' +
25196         '</style>';
25197         
25198         st += '<meta name="google" content="notranslate">';
25199         
25200         var cls = 'notranslate roo-htmleditor-body';
25201         
25202         if(this.bodyCls.length){
25203             cls += ' ' + this.bodyCls;
25204         }
25205         
25206         return '<html  class="notranslate" translate="no"><head>' + st  +
25207             //<style type="text/css">' +
25208             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25209             //'</style>' +
25210             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25211     },
25212
25213     // private
25214     onRender : function(ct, position)
25215     {
25216         var _t = this;
25217         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25218         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25219         
25220         
25221         this.el.dom.style.border = '0 none';
25222         this.el.dom.setAttribute('tabIndex', -1);
25223         this.el.addClass('x-hidden hide');
25224         
25225         
25226         
25227         if(Roo.isIE){ // fix IE 1px bogus margin
25228             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25229         }
25230        
25231         
25232         this.frameId = Roo.id();
25233         
25234          
25235         
25236         var iframe = this.owner.wrap.createChild({
25237             tag: 'iframe',
25238             cls: 'form-control', // bootstrap..
25239             id: this.frameId,
25240             name: this.frameId,
25241             frameBorder : 'no',
25242             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25243         }, this.el
25244         );
25245         
25246         
25247         this.iframe = iframe.dom;
25248
25249         this.assignDocWin();
25250         
25251         this.doc.designMode = 'on';
25252        
25253         this.doc.open();
25254         this.doc.write(this.getDocMarkup());
25255         this.doc.close();
25256
25257         
25258         var task = { // must defer to wait for browser to be ready
25259             run : function(){
25260                 //console.log("run task?" + this.doc.readyState);
25261                 this.assignDocWin();
25262                 if(this.doc.body || this.doc.readyState == 'complete'){
25263                     try {
25264                         this.doc.designMode="on";
25265                         
25266                     } catch (e) {
25267                         return;
25268                     }
25269                     Roo.TaskMgr.stop(task);
25270                     this.initEditor.defer(10, this);
25271                 }
25272             },
25273             interval : 10,
25274             duration: 10000,
25275             scope: this
25276         };
25277         Roo.TaskMgr.start(task);
25278
25279     },
25280
25281     // private
25282     onResize : function(w, h)
25283     {
25284          Roo.log('resize: ' +w + ',' + h );
25285         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25286         if(!this.iframe){
25287             return;
25288         }
25289         if(typeof w == 'number'){
25290             
25291             this.iframe.style.width = w + 'px';
25292         }
25293         if(typeof h == 'number'){
25294             
25295             this.iframe.style.height = h + 'px';
25296             if(this.doc){
25297                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25298             }
25299         }
25300         
25301     },
25302
25303     /**
25304      * Toggles the editor between standard and source edit mode.
25305      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25306      */
25307     toggleSourceEdit : function(sourceEditMode){
25308         
25309         this.sourceEditMode = sourceEditMode === true;
25310         
25311         if(this.sourceEditMode){
25312  
25313             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
25314             
25315         }else{
25316             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25317             //this.iframe.className = '';
25318             this.deferFocus();
25319         }
25320         //this.setSize(this.owner.wrap.getSize());
25321         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25322     },
25323
25324     
25325   
25326
25327     /**
25328      * Protected method that will not generally be called directly. If you need/want
25329      * custom HTML cleanup, this is the method you should override.
25330      * @param {String} html The HTML to be cleaned
25331      * return {String} The cleaned HTML
25332      */
25333     cleanHtml : function(html)
25334     {
25335         html = String(html);
25336         if(html.length > 5){
25337             if(Roo.isSafari){ // strip safari nonsense
25338                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25339             }
25340         }
25341         if(html == '&nbsp;'){
25342             html = '';
25343         }
25344         return html;
25345     },
25346
25347     /**
25348      * HTML Editor -> Textarea
25349      * Protected method that will not generally be called directly. Syncs the contents
25350      * of the editor iframe with the textarea.
25351      */
25352     syncValue : function()
25353     {
25354         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25355         if(this.initialized){
25356             
25357             this.undoManager.addEvent();
25358
25359             
25360             var bd = (this.doc.body || this.doc.documentElement);
25361            
25362             
25363             var sel = this.win.getSelection();
25364             
25365             var div = document.createElement('div');
25366             div.innerHTML = bd.innerHTML;
25367             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25368             if (gtx.length > 0) {
25369                 var rm = gtx.item(0).parentNode;
25370                 rm.parentNode.removeChild(rm);
25371             }
25372             
25373            
25374             if (this.enableBlocks) {
25375                 new Roo.htmleditor.FilterBlock({ node : div });
25376             }
25377             //?? tidy?
25378             var tidy = new Roo.htmleditor.TidySerializer({
25379                 inner:  true
25380             });
25381             var html  = tidy.serialize(div);
25382             
25383             
25384             if(Roo.isSafari){
25385                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25386                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25387                 if(m && m[1]){
25388                     html = '<div style="'+m[0]+'">' + html + '</div>';
25389                 }
25390             }
25391             html = this.cleanHtml(html);
25392             // fix up the special chars.. normaly like back quotes in word...
25393             // however we do not want to do this with chinese..
25394             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25395                 
25396                 var cc = match.charCodeAt();
25397
25398                 // Get the character value, handling surrogate pairs
25399                 if (match.length == 2) {
25400                     // It's a surrogate pair, calculate the Unicode code point
25401                     var high = match.charCodeAt(0) - 0xD800;
25402                     var low  = match.charCodeAt(1) - 0xDC00;
25403                     cc = (high * 0x400) + low + 0x10000;
25404                 }  else if (
25405                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25406                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25407                     (cc >= 0xf900 && cc < 0xfb00 )
25408                 ) {
25409                         return match;
25410                 }  
25411          
25412                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25413                 return "&#" + cc + ";";
25414                 
25415                 
25416             });
25417             
25418             
25419              
25420             if(this.owner.fireEvent('beforesync', this, html) !== false){
25421                 this.el.dom.value = html;
25422                 this.owner.fireEvent('sync', this, html);
25423             }
25424         }
25425     },
25426
25427     /**
25428      * TEXTAREA -> EDITABLE
25429      * Protected method that will not generally be called directly. Pushes the value of the textarea
25430      * into the iframe editor.
25431      */
25432     pushValue : function()
25433     {
25434         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25435         if(this.initialized){
25436             var v = this.el.dom.value.trim();
25437             
25438             
25439             if(this.owner.fireEvent('beforepush', this, v) !== false){
25440                 var d = (this.doc.body || this.doc.documentElement);
25441                 d.innerHTML = v;
25442                  
25443                 this.el.dom.value = d.innerHTML;
25444                 this.owner.fireEvent('push', this, v);
25445             }
25446             if (this.autoClean) {
25447                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25448                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25449             }
25450             if (this.enableBlocks) {
25451                 Roo.htmleditor.Block.initAll(this.doc.body);
25452             }
25453             
25454             this.updateLanguage();
25455             
25456             var lc = this.doc.body.lastChild;
25457             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25458                 // add an extra line at the end.
25459                 this.doc.body.appendChild(this.doc.createElement('br'));
25460             }
25461             
25462             
25463         }
25464     },
25465
25466     // private
25467     deferFocus : function(){
25468         this.focus.defer(10, this);
25469     },
25470
25471     // doc'ed in Field
25472     focus : function(){
25473         if(this.win && !this.sourceEditMode){
25474             this.win.focus();
25475         }else{
25476             this.el.focus();
25477         }
25478     },
25479     
25480     assignDocWin: function()
25481     {
25482         var iframe = this.iframe;
25483         
25484          if(Roo.isIE){
25485             this.doc = iframe.contentWindow.document;
25486             this.win = iframe.contentWindow;
25487         } else {
25488 //            if (!Roo.get(this.frameId)) {
25489 //                return;
25490 //            }
25491 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25492 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25493             
25494             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25495                 return;
25496             }
25497             
25498             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25499             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25500         }
25501     },
25502     
25503     // private
25504     initEditor : function(){
25505         //console.log("INIT EDITOR");
25506         this.assignDocWin();
25507         
25508         
25509         
25510         this.doc.designMode="on";
25511         this.doc.open();
25512         this.doc.write(this.getDocMarkup());
25513         this.doc.close();
25514         
25515         var dbody = (this.doc.body || this.doc.documentElement);
25516         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25517         // this copies styles from the containing element into thsi one..
25518         // not sure why we need all of this..
25519         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25520         
25521         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25522         //ss['background-attachment'] = 'fixed'; // w3c
25523         dbody.bgProperties = 'fixed'; // ie
25524         dbody.setAttribute("translate", "no");
25525         
25526         //Roo.DomHelper.applyStyles(dbody, ss);
25527         Roo.EventManager.on(this.doc, {
25528              
25529             'mouseup': this.onEditorEvent,
25530             'dblclick': this.onEditorEvent,
25531             'click': this.onEditorEvent,
25532             'keyup': this.onEditorEvent,
25533             
25534             buffer:100,
25535             scope: this
25536         });
25537         Roo.EventManager.on(this.doc, {
25538             'paste': this.onPasteEvent,
25539             scope : this
25540         });
25541         if(Roo.isGecko){
25542             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25543         }
25544         //??? needed???
25545         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25546             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25547         }
25548         this.initialized = true;
25549
25550         
25551         // initialize special key events - enter
25552         new Roo.htmleditor.KeyEnter({core : this});
25553         
25554          
25555         
25556         this.owner.fireEvent('initialize', this);
25557         this.pushValue();
25558     },
25559     // this is to prevent a href clicks resulting in a redirect?
25560    
25561     onPasteEvent : function(e,v)
25562     {
25563         // I think we better assume paste is going to be a dirty load of rubish from word..
25564         
25565         // even pasting into a 'email version' of this widget will have to clean up that mess.
25566         var cd = (e.browserEvent.clipboardData || window.clipboardData);
25567         
25568         // check what type of paste - if it's an image, then handle it differently.
25569         if (cd.files && cd.files.length > 0) {
25570             // pasting images?
25571             var urlAPI = (window.createObjectURL && window) || 
25572                 (window.URL && URL.revokeObjectURL && URL) || 
25573                 (window.webkitURL && webkitURL);
25574     
25575             var url = urlAPI.createObjectURL( cd.files[0]);
25576             this.insertAtCursor('<img src=" + url + ">');
25577             return false;
25578         }
25579         if (cd.types.indexOf('text/html') < 0 ) {
25580             return false;
25581         }
25582         var images = [];
25583         var html = cd.getData('text/html'); // clipboard event
25584         if (cd.types.indexOf('text/rtf') > -1) {
25585             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25586             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25587         }
25588         Roo.log(images);
25589         //Roo.log(imgs);
25590         // fixme..
25591         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
25592                        .map(function(g) { return g.toDataURL(); })
25593                        .filter(function(g) { return g != 'about:blank'; });
25594         
25595         
25596         html = this.cleanWordChars(html);
25597         
25598         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25599         
25600         
25601         var sn = this.getParentElement();
25602         // check if d contains a table, and prevent nesting??
25603         //Roo.log(d.getElementsByTagName('table'));
25604         //Roo.log(sn);
25605         //Roo.log(sn.closest('table'));
25606         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25607             e.preventDefault();
25608             this.insertAtCursor("You can not nest tables");
25609             //Roo.log("prevent?"); // fixme - 
25610             return false;
25611         }
25612         
25613         if (images.length > 0) {
25614             Roo.each(d.getElementsByTagName('img'), function(img, i) {
25615                 img.setAttribute('src', images[i]);
25616             });
25617         }
25618         if (this.autoClean) {
25619             new Roo.htmleditor.FilterStyleToTag({ node : d });
25620             new Roo.htmleditor.FilterAttributes({
25621                 node : d,
25622                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan'],
25623                 attrib_clean : ['href', 'src' ] 
25624             });
25625             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25626             // should be fonts..
25627             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25628             new Roo.htmleditor.FilterParagraph({ node : d });
25629             new Roo.htmleditor.FilterSpan({ node : d });
25630             new Roo.htmleditor.FilterLongBr({ node : d });
25631         }
25632         if (this.enableBlocks) {
25633                 
25634             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25635                 if (img.closest('figure')) { // assume!! that it's aready
25636                     return;
25637                 }
25638                 var fig  = new Roo.htmleditor.BlockFigure({
25639                     image_src  : img.src
25640                 });
25641                 fig.updateElement(img); // replace it..
25642                 
25643             });
25644         }
25645         
25646         
25647         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
25648         if (this.enableBlocks) {
25649             Roo.htmleditor.Block.initAll(this.doc.body);
25650         }
25651         
25652         
25653         e.preventDefault();
25654         return false;
25655         // default behaveiour should be our local cleanup paste? (optional?)
25656         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25657         //this.owner.fireEvent('paste', e, v);
25658     },
25659     // private
25660     onDestroy : function(){
25661         
25662         
25663         
25664         if(this.rendered){
25665             
25666             //for (var i =0; i < this.toolbars.length;i++) {
25667             //    // fixme - ask toolbars for heights?
25668             //    this.toolbars[i].onDestroy();
25669            // }
25670             
25671             //this.wrap.dom.innerHTML = '';
25672             //this.wrap.remove();
25673         }
25674     },
25675
25676     // private
25677     onFirstFocus : function(){
25678         
25679         this.assignDocWin();
25680         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25681         
25682         this.activated = true;
25683          
25684     
25685         if(Roo.isGecko){ // prevent silly gecko errors
25686             this.win.focus();
25687             var s = this.win.getSelection();
25688             if(!s.focusNode || s.focusNode.nodeType != 3){
25689                 var r = s.getRangeAt(0);
25690                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25691                 r.collapse(true);
25692                 this.deferFocus();
25693             }
25694             try{
25695                 this.execCmd('useCSS', true);
25696                 this.execCmd('styleWithCSS', false);
25697             }catch(e){}
25698         }
25699         this.owner.fireEvent('activate', this);
25700     },
25701
25702     // private
25703     adjustFont: function(btn){
25704         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25705         //if(Roo.isSafari){ // safari
25706         //    adjust *= 2;
25707        // }
25708         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25709         if(Roo.isSafari){ // safari
25710             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25711             v =  (v < 10) ? 10 : v;
25712             v =  (v > 48) ? 48 : v;
25713             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25714             
25715         }
25716         
25717         
25718         v = Math.max(1, v+adjust);
25719         
25720         this.execCmd('FontSize', v  );
25721     },
25722
25723     onEditorEvent : function(e)
25724     {
25725          
25726         
25727         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25728             return; // we do not handle this.. (undo manager does..)
25729         }
25730         // in theory this detects if the last element is not a br, then we try and do that.
25731         // its so clicking in space at bottom triggers adding a br and moving the cursor.
25732         if (e &&
25733             e.target.nodeName == 'BODY' &&
25734             e.type == "mouseup" &&
25735             this.doc.body.lastChild
25736            ) {
25737             var lc = this.doc.body.lastChild;
25738             // gtx-trans is google translate plugin adding crap.
25739             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25740                 lc = lc.previousSibling;
25741             }
25742             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25743             // if last element is <BR> - then dont do anything.
25744             
25745                 var ns = this.doc.createElement('br');
25746                 this.doc.body.appendChild(ns);
25747                 range = this.doc.createRange();
25748                 range.setStartAfter(ns);
25749                 range.collapse(true);
25750                 var sel = this.win.getSelection();
25751                 sel.removeAllRanges();
25752                 sel.addRange(range);
25753             }
25754         }
25755         
25756         
25757         
25758         this.fireEditorEvent(e);
25759       //  this.updateToolbar();
25760         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25761     },
25762     
25763     fireEditorEvent: function(e)
25764     {
25765         this.owner.fireEvent('editorevent', this, e);
25766     },
25767
25768     insertTag : function(tg)
25769     {
25770         // could be a bit smarter... -> wrap the current selected tRoo..
25771         if (tg.toLowerCase() == 'span' ||
25772             tg.toLowerCase() == 'code' ||
25773             tg.toLowerCase() == 'sup' ||
25774             tg.toLowerCase() == 'sub' 
25775             ) {
25776             
25777             range = this.createRange(this.getSelection());
25778             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25779             wrappingNode.appendChild(range.extractContents());
25780             range.insertNode(wrappingNode);
25781
25782             return;
25783             
25784             
25785             
25786         }
25787         this.execCmd("formatblock",   tg);
25788         this.undoManager.addEvent(); 
25789     },
25790     
25791     insertText : function(txt)
25792     {
25793         
25794         
25795         var range = this.createRange();
25796         range.deleteContents();
25797                //alert(Sender.getAttribute('label'));
25798                
25799         range.insertNode(this.doc.createTextNode(txt));
25800         this.undoManager.addEvent();
25801     } ,
25802     
25803      
25804
25805     /**
25806      * Executes a Midas editor command on the editor document and performs necessary focus and
25807      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25808      * @param {String} cmd The Midas command
25809      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25810      */
25811     relayCmd : function(cmd, value)
25812     {
25813         
25814         switch (cmd) {
25815             case 'justifyleft':
25816             case 'justifyright':
25817             case 'justifycenter':
25818                 // if we are in a cell, then we will adjust the
25819                 var n = this.getParentElement();
25820                 var td = n.closest('td');
25821                 if (td) {
25822                     var bl = Roo.htmleditor.Block.factory(td);
25823                     bl.textAlign = cmd.replace('justify','');
25824                     bl.updateElement();
25825                     this.owner.fireEvent('editorevent', this);
25826                     return;
25827                 }
25828                 this.execCmd('styleWithCSS', true); // 
25829                 break;
25830             case 'bold':
25831             case 'italic':
25832                 // if there is no selection, then we insert, and set the curson inside it..
25833                 this.execCmd('styleWithCSS', false); 
25834                 break;
25835                 
25836         
25837             default:
25838                 break;
25839         }
25840         
25841         
25842         this.win.focus();
25843         this.execCmd(cmd, value);
25844         this.owner.fireEvent('editorevent', this);
25845         //this.updateToolbar();
25846         this.owner.deferFocus();
25847     },
25848
25849     /**
25850      * Executes a Midas editor command directly on the editor document.
25851      * For visual commands, you should use {@link #relayCmd} instead.
25852      * <b>This should only be called after the editor is initialized.</b>
25853      * @param {String} cmd The Midas command
25854      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25855      */
25856     execCmd : function(cmd, value){
25857         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25858         this.syncValue();
25859     },
25860  
25861  
25862    
25863     /**
25864      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25865      * to insert tRoo.
25866      * @param {String} text | dom node.. 
25867      */
25868     insertAtCursor : function(text)
25869     {
25870         
25871         if(!this.activated){
25872             return;
25873         }
25874          
25875         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25876             this.win.focus();
25877             
25878             
25879             // from jquery ui (MIT licenced)
25880             var range, node;
25881             var win = this.win;
25882             
25883             if (win.getSelection && win.getSelection().getRangeAt) {
25884                 
25885                 // delete the existing?
25886                 
25887                 this.createRange(this.getSelection()).deleteContents();
25888                 range = win.getSelection().getRangeAt(0);
25889                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25890                 range.insertNode(node);
25891                 range = range.cloneRange();
25892                 range.collapse(false);
25893                  
25894                 win.getSelection().removeAllRanges();
25895                 win.getSelection().addRange(range);
25896                 
25897                 
25898                 
25899             } else if (win.document.selection && win.document.selection.createRange) {
25900                 // no firefox support
25901                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25902                 win.document.selection.createRange().pasteHTML(txt);
25903             
25904             } else {
25905                 // no firefox support
25906                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25907                 this.execCmd('InsertHTML', txt);
25908             } 
25909             this.syncValue();
25910             
25911             this.deferFocus();
25912         }
25913     },
25914  // private
25915     mozKeyPress : function(e){
25916         if(e.ctrlKey){
25917             var c = e.getCharCode(), cmd;
25918           
25919             if(c > 0){
25920                 c = String.fromCharCode(c).toLowerCase();
25921                 switch(c){
25922                     case 'b':
25923                         cmd = 'bold';
25924                         break;
25925                     case 'i':
25926                         cmd = 'italic';
25927                         break;
25928                     
25929                     case 'u':
25930                         cmd = 'underline';
25931                         break;
25932                     
25933                     //case 'v':
25934                       //  this.cleanUpPaste.defer(100, this);
25935                       //  return;
25936                         
25937                 }
25938                 if(cmd){
25939                     
25940                     this.relayCmd(cmd);
25941                     //this.win.focus();
25942                     //this.execCmd(cmd);
25943                     //this.deferFocus();
25944                     e.preventDefault();
25945                 }
25946                 
25947             }
25948         }
25949     },
25950
25951     // private
25952     fixKeys : function(){ // load time branching for fastest keydown performance
25953         
25954         
25955         if(Roo.isIE){
25956             return function(e){
25957                 var k = e.getKey(), r;
25958                 if(k == e.TAB){
25959                     e.stopEvent();
25960                     r = this.doc.selection.createRange();
25961                     if(r){
25962                         r.collapse(true);
25963                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25964                         this.deferFocus();
25965                     }
25966                     return;
25967                 }
25968                 /// this is handled by Roo.htmleditor.KeyEnter
25969                  /*
25970                 if(k == e.ENTER){
25971                     r = this.doc.selection.createRange();
25972                     if(r){
25973                         var target = r.parentElement();
25974                         if(!target || target.tagName.toLowerCase() != 'li'){
25975                             e.stopEvent();
25976                             r.pasteHTML('<br/>');
25977                             r.collapse(false);
25978                             r.select();
25979                         }
25980                     }
25981                 }
25982                 */
25983                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25984                 //    this.cleanUpPaste.defer(100, this);
25985                 //    return;
25986                 //}
25987                 
25988                 
25989             };
25990         }else if(Roo.isOpera){
25991             return function(e){
25992                 var k = e.getKey();
25993                 if(k == e.TAB){
25994                     e.stopEvent();
25995                     this.win.focus();
25996                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25997                     this.deferFocus();
25998                 }
25999                
26000                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26001                 //    this.cleanUpPaste.defer(100, this);
26002                  //   return;
26003                 //}
26004                 
26005             };
26006         }else if(Roo.isSafari){
26007             return function(e){
26008                 var k = e.getKey();
26009                 
26010                 if(k == e.TAB){
26011                     e.stopEvent();
26012                     this.execCmd('InsertText','\t');
26013                     this.deferFocus();
26014                     return;
26015                 }
26016                  this.mozKeyPress(e);
26017                 
26018                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26019                  //   this.cleanUpPaste.defer(100, this);
26020                  //   return;
26021                // }
26022                 
26023              };
26024         }
26025     }(),
26026     
26027     getAllAncestors: function()
26028     {
26029         var p = this.getSelectedNode();
26030         var a = [];
26031         if (!p) {
26032             a.push(p); // push blank onto stack..
26033             p = this.getParentElement();
26034         }
26035         
26036         
26037         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26038             a.push(p);
26039             p = p.parentNode;
26040         }
26041         a.push(this.doc.body);
26042         return a;
26043     },
26044     lastSel : false,
26045     lastSelNode : false,
26046     
26047     
26048     getSelection : function() 
26049     {
26050         this.assignDocWin();
26051         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26052     },
26053     /**
26054      * Select a dom node
26055      * @param {DomElement} node the node to select
26056      */
26057     selectNode : function(node, collapse)
26058     {
26059         var nodeRange = node.ownerDocument.createRange();
26060         try {
26061             nodeRange.selectNode(node);
26062         } catch (e) {
26063             nodeRange.selectNodeContents(node);
26064         }
26065         if (collapse === true) {
26066             nodeRange.collapse(true);
26067         }
26068         //
26069         var s = this.win.getSelection();
26070         s.removeAllRanges();
26071         s.addRange(nodeRange);
26072     },
26073     
26074     getSelectedNode: function() 
26075     {
26076         // this may only work on Gecko!!!
26077         
26078         // should we cache this!!!!
26079         
26080          
26081          
26082         var range = this.createRange(this.getSelection()).cloneRange();
26083         
26084         if (Roo.isIE) {
26085             var parent = range.parentElement();
26086             while (true) {
26087                 var testRange = range.duplicate();
26088                 testRange.moveToElementText(parent);
26089                 if (testRange.inRange(range)) {
26090                     break;
26091                 }
26092                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26093                     break;
26094                 }
26095                 parent = parent.parentElement;
26096             }
26097             return parent;
26098         }
26099         
26100         // is ancestor a text element.
26101         var ac =  range.commonAncestorContainer;
26102         if (ac.nodeType == 3) {
26103             ac = ac.parentNode;
26104         }
26105         
26106         var ar = ac.childNodes;
26107          
26108         var nodes = [];
26109         var other_nodes = [];
26110         var has_other_nodes = false;
26111         for (var i=0;i<ar.length;i++) {
26112             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26113                 continue;
26114             }
26115             // fullly contained node.
26116             
26117             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26118                 nodes.push(ar[i]);
26119                 continue;
26120             }
26121             
26122             // probably selected..
26123             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26124                 other_nodes.push(ar[i]);
26125                 continue;
26126             }
26127             // outer..
26128             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26129                 continue;
26130             }
26131             
26132             
26133             has_other_nodes = true;
26134         }
26135         if (!nodes.length && other_nodes.length) {
26136             nodes= other_nodes;
26137         }
26138         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26139             return false;
26140         }
26141         
26142         return nodes[0];
26143     },
26144     
26145     
26146     createRange: function(sel)
26147     {
26148         // this has strange effects when using with 
26149         // top toolbar - not sure if it's a great idea.
26150         //this.editor.contentWindow.focus();
26151         if (typeof sel != "undefined") {
26152             try {
26153                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26154             } catch(e) {
26155                 return this.doc.createRange();
26156             }
26157         } else {
26158             return this.doc.createRange();
26159         }
26160     },
26161     getParentElement: function()
26162     {
26163         
26164         this.assignDocWin();
26165         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26166         
26167         var range = this.createRange(sel);
26168          
26169         try {
26170             var p = range.commonAncestorContainer;
26171             while (p.nodeType == 3) { // text node
26172                 p = p.parentNode;
26173             }
26174             return p;
26175         } catch (e) {
26176             return null;
26177         }
26178     
26179     },
26180     /***
26181      *
26182      * Range intersection.. the hard stuff...
26183      *  '-1' = before
26184      *  '0' = hits..
26185      *  '1' = after.
26186      *         [ -- selected range --- ]
26187      *   [fail]                        [fail]
26188      *
26189      *    basically..
26190      *      if end is before start or  hits it. fail.
26191      *      if start is after end or hits it fail.
26192      *
26193      *   if either hits (but other is outside. - then it's not 
26194      *   
26195      *    
26196      **/
26197     
26198     
26199     // @see http://www.thismuchiknow.co.uk/?p=64.
26200     rangeIntersectsNode : function(range, node)
26201     {
26202         var nodeRange = node.ownerDocument.createRange();
26203         try {
26204             nodeRange.selectNode(node);
26205         } catch (e) {
26206             nodeRange.selectNodeContents(node);
26207         }
26208     
26209         var rangeStartRange = range.cloneRange();
26210         rangeStartRange.collapse(true);
26211     
26212         var rangeEndRange = range.cloneRange();
26213         rangeEndRange.collapse(false);
26214     
26215         var nodeStartRange = nodeRange.cloneRange();
26216         nodeStartRange.collapse(true);
26217     
26218         var nodeEndRange = nodeRange.cloneRange();
26219         nodeEndRange.collapse(false);
26220     
26221         return rangeStartRange.compareBoundaryPoints(
26222                  Range.START_TO_START, nodeEndRange) == -1 &&
26223                rangeEndRange.compareBoundaryPoints(
26224                  Range.START_TO_START, nodeStartRange) == 1;
26225         
26226          
26227     },
26228     rangeCompareNode : function(range, node)
26229     {
26230         var nodeRange = node.ownerDocument.createRange();
26231         try {
26232             nodeRange.selectNode(node);
26233         } catch (e) {
26234             nodeRange.selectNodeContents(node);
26235         }
26236         
26237         
26238         range.collapse(true);
26239     
26240         nodeRange.collapse(true);
26241      
26242         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26243         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26244          
26245         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26246         
26247         var nodeIsBefore   =  ss == 1;
26248         var nodeIsAfter    = ee == -1;
26249         
26250         if (nodeIsBefore && nodeIsAfter) {
26251             return 0; // outer
26252         }
26253         if (!nodeIsBefore && nodeIsAfter) {
26254             return 1; //right trailed.
26255         }
26256         
26257         if (nodeIsBefore && !nodeIsAfter) {
26258             return 2;  // left trailed.
26259         }
26260         // fully contined.
26261         return 3;
26262     },
26263  
26264     cleanWordChars : function(input) {// change the chars to hex code
26265         
26266        var swapCodes  = [ 
26267             [    8211, "&#8211;" ], 
26268             [    8212, "&#8212;" ], 
26269             [    8216,  "'" ],  
26270             [    8217, "'" ],  
26271             [    8220, '"' ],  
26272             [    8221, '"' ],  
26273             [    8226, "*" ],  
26274             [    8230, "..." ]
26275         ]; 
26276         var output = input;
26277         Roo.each(swapCodes, function(sw) { 
26278             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26279             
26280             output = output.replace(swapper, sw[1]);
26281         });
26282         
26283         return output;
26284     },
26285     
26286      
26287     
26288         
26289     
26290     cleanUpChild : function (node)
26291     {
26292         
26293         new Roo.htmleditor.FilterComment({node : node});
26294         new Roo.htmleditor.FilterAttributes({
26295                 node : node,
26296                 attrib_black : this.ablack,
26297                 attrib_clean : this.aclean,
26298                 style_white : this.cwhite,
26299                 style_black : this.cblack
26300         });
26301         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26302         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26303          
26304         
26305     },
26306     
26307     /**
26308      * Clean up MS wordisms...
26309      * @deprecated - use filter directly
26310      */
26311     cleanWord : function(node)
26312     {
26313         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26314         
26315     },
26316    
26317     
26318     /**
26319
26320      * @deprecated - use filters
26321      */
26322     cleanTableWidths : function(node)
26323     {
26324         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26325         
26326  
26327     },
26328     
26329      
26330         
26331     applyBlacklists : function()
26332     {
26333         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26334         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26335         
26336         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26337         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26338         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26339         
26340         this.white = [];
26341         this.black = [];
26342         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26343             if (b.indexOf(tag) > -1) {
26344                 return;
26345             }
26346             this.white.push(tag);
26347             
26348         }, this);
26349         
26350         Roo.each(w, function(tag) {
26351             if (b.indexOf(tag) > -1) {
26352                 return;
26353             }
26354             if (this.white.indexOf(tag) > -1) {
26355                 return;
26356             }
26357             this.white.push(tag);
26358             
26359         }, this);
26360         
26361         
26362         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26363             if (w.indexOf(tag) > -1) {
26364                 return;
26365             }
26366             this.black.push(tag);
26367             
26368         }, this);
26369         
26370         Roo.each(b, function(tag) {
26371             if (w.indexOf(tag) > -1) {
26372                 return;
26373             }
26374             if (this.black.indexOf(tag) > -1) {
26375                 return;
26376             }
26377             this.black.push(tag);
26378             
26379         }, this);
26380         
26381         
26382         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26383         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26384         
26385         this.cwhite = [];
26386         this.cblack = [];
26387         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26388             if (b.indexOf(tag) > -1) {
26389                 return;
26390             }
26391             this.cwhite.push(tag);
26392             
26393         }, this);
26394         
26395         Roo.each(w, function(tag) {
26396             if (b.indexOf(tag) > -1) {
26397                 return;
26398             }
26399             if (this.cwhite.indexOf(tag) > -1) {
26400                 return;
26401             }
26402             this.cwhite.push(tag);
26403             
26404         }, this);
26405         
26406         
26407         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26408             if (w.indexOf(tag) > -1) {
26409                 return;
26410             }
26411             this.cblack.push(tag);
26412             
26413         }, this);
26414         
26415         Roo.each(b, function(tag) {
26416             if (w.indexOf(tag) > -1) {
26417                 return;
26418             }
26419             if (this.cblack.indexOf(tag) > -1) {
26420                 return;
26421             }
26422             this.cblack.push(tag);
26423             
26424         }, this);
26425     },
26426     
26427     setStylesheets : function(stylesheets)
26428     {
26429         if(typeof(stylesheets) == 'string'){
26430             Roo.get(this.iframe.contentDocument.head).createChild({
26431                 tag : 'link',
26432                 rel : 'stylesheet',
26433                 type : 'text/css',
26434                 href : stylesheets
26435             });
26436             
26437             return;
26438         }
26439         var _this = this;
26440      
26441         Roo.each(stylesheets, function(s) {
26442             if(!s.length){
26443                 return;
26444             }
26445             
26446             Roo.get(_this.iframe.contentDocument.head).createChild({
26447                 tag : 'link',
26448                 rel : 'stylesheet',
26449                 type : 'text/css',
26450                 href : s
26451             });
26452         });
26453
26454         
26455     },
26456     
26457     
26458     updateLanguage : function()
26459     {
26460         if (!this.iframe || !this.iframe.contentDocument) {
26461             return;
26462         }
26463         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26464     },
26465     
26466     
26467     removeStylesheets : function()
26468     {
26469         var _this = this;
26470         
26471         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26472             s.remove();
26473         });
26474     },
26475     
26476     setStyle : function(style)
26477     {
26478         Roo.get(this.iframe.contentDocument.head).createChild({
26479             tag : 'style',
26480             type : 'text/css',
26481             html : style
26482         });
26483
26484         return;
26485     }
26486     
26487     // hide stuff that is not compatible
26488     /**
26489      * @event blur
26490      * @hide
26491      */
26492     /**
26493      * @event change
26494      * @hide
26495      */
26496     /**
26497      * @event focus
26498      * @hide
26499      */
26500     /**
26501      * @event specialkey
26502      * @hide
26503      */
26504     /**
26505      * @cfg {String} fieldClass @hide
26506      */
26507     /**
26508      * @cfg {String} focusClass @hide
26509      */
26510     /**
26511      * @cfg {String} autoCreate @hide
26512      */
26513     /**
26514      * @cfg {String} inputType @hide
26515      */
26516     /**
26517      * @cfg {String} invalidClass @hide
26518      */
26519     /**
26520      * @cfg {String} invalidText @hide
26521      */
26522     /**
26523      * @cfg {String} msgFx @hide
26524      */
26525     /**
26526      * @cfg {String} validateOnBlur @hide
26527      */
26528 });
26529
26530 Roo.HtmlEditorCore.white = [
26531         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26532         
26533        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
26534        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
26535        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
26536        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
26537        'TABLE',   'UL',         'XMP', 
26538        
26539        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
26540       'THEAD',   'TR', 
26541      
26542       'DIR', 'MENU', 'OL', 'UL', 'DL',
26543        
26544       'EMBED',  'OBJECT'
26545 ];
26546
26547
26548 Roo.HtmlEditorCore.black = [
26549     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26550         'APPLET', // 
26551         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
26552         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
26553         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
26554         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
26555         //'FONT' // CLEAN LATER..
26556         'COLGROUP', 'COL'   // messy tables.
26557         
26558         
26559 ];
26560 Roo.HtmlEditorCore.clean = [ // ?? needed???
26561      'SCRIPT', 'STYLE', 'TITLE', 'XML'
26562 ];
26563 Roo.HtmlEditorCore.tag_remove = [
26564     'FONT', 'TBODY'  
26565 ];
26566 // attributes..
26567
26568 Roo.HtmlEditorCore.ablack = [
26569     'on'
26570 ];
26571     
26572 Roo.HtmlEditorCore.aclean = [ 
26573     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26574 ];
26575
26576 // protocols..
26577 Roo.HtmlEditorCore.pwhite= [
26578         'http',  'https',  'mailto'
26579 ];
26580
26581 // white listed style attributes.
26582 Roo.HtmlEditorCore.cwhite= [
26583       //  'text-align', /// default is to allow most things..
26584       
26585          
26586 //        'font-size'//??
26587 ];
26588
26589 // black listed style attributes.
26590 Roo.HtmlEditorCore.cblack= [
26591       //  'font-size' -- this can be set by the project 
26592 ];
26593
26594
26595
26596
26597     //<script type="text/javascript">
26598
26599 /*
26600  * Ext JS Library 1.1.1
26601  * Copyright(c) 2006-2007, Ext JS, LLC.
26602  * Licence LGPL
26603  * 
26604  */
26605  
26606  
26607 Roo.form.HtmlEditor = function(config){
26608     
26609     
26610     
26611     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26612     
26613     if (!this.toolbars) {
26614         this.toolbars = [];
26615     }
26616     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26617     
26618     
26619 };
26620
26621 /**
26622  * @class Roo.form.HtmlEditor
26623  * @extends Roo.form.Field
26624  * Provides a lightweight HTML Editor component.
26625  *
26626  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26627  * 
26628  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26629  * supported by this editor.</b><br/><br/>
26630  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26631  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26632  */
26633 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26634     /**
26635      * @cfg {Boolean} clearUp
26636      */
26637     clearUp : true,
26638       /**
26639      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26640      */
26641     toolbars : false,
26642    
26643      /**
26644      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26645      *                        Roo.resizable.
26646      */
26647     resizable : false,
26648      /**
26649      * @cfg {Number} height (in pixels)
26650      */   
26651     height: 300,
26652    /**
26653      * @cfg {Number} width (in pixels)
26654      */   
26655     width: 500,
26656     
26657     /**
26658      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
26659      * 
26660      */
26661     stylesheets: false,
26662     
26663     
26664      /**
26665      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26666      * 
26667      */
26668     cblack: false,
26669     /**
26670      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26671      * 
26672      */
26673     cwhite: false,
26674     
26675      /**
26676      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26677      * 
26678      */
26679     black: false,
26680     /**
26681      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26682      * 
26683      */
26684     white: false,
26685     /**
26686      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26687      */
26688     allowComments: false,
26689     /**
26690      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26691      */
26692     enableBlocks : true,
26693     
26694     /**
26695      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26696      *         if you are doing an email editor, this probably needs disabling, it's designed
26697      */
26698     autoClean: true,
26699     /**
26700      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
26701      */
26702     bodyCls : '',
26703     /**
26704      * @cfg {String} language default en - language of text (usefull for rtl languages)
26705      * 
26706      */
26707     language: 'en',
26708     
26709      
26710     // id of frame..
26711     frameId: false,
26712     
26713     // private properties
26714     validationEvent : false,
26715     deferHeight: true,
26716     initialized : false,
26717     activated : false,
26718     
26719     onFocus : Roo.emptyFn,
26720     iframePad:3,
26721     hideMode:'offsets',
26722     
26723     actionMode : 'container', // defaults to hiding it...
26724     
26725     defaultAutoCreate : { // modified by initCompnoent..
26726         tag: "textarea",
26727         style:"width:500px;height:300px;",
26728         autocomplete: "new-password"
26729     },
26730
26731     // private
26732     initComponent : function(){
26733         this.addEvents({
26734             /**
26735              * @event initialize
26736              * Fires when the editor is fully initialized (including the iframe)
26737              * @param {HtmlEditor} this
26738              */
26739             initialize: true,
26740             /**
26741              * @event activate
26742              * Fires when the editor is first receives the focus. Any insertion must wait
26743              * until after this event.
26744              * @param {HtmlEditor} this
26745              */
26746             activate: true,
26747              /**
26748              * @event beforesync
26749              * Fires before the textarea is updated with content from the editor iframe. Return false
26750              * to cancel the sync.
26751              * @param {HtmlEditor} this
26752              * @param {String} html
26753              */
26754             beforesync: true,
26755              /**
26756              * @event beforepush
26757              * Fires before the iframe editor is updated with content from the textarea. Return false
26758              * to cancel the push.
26759              * @param {HtmlEditor} this
26760              * @param {String} html
26761              */
26762             beforepush: true,
26763              /**
26764              * @event sync
26765              * Fires when the textarea is updated with content from the editor iframe.
26766              * @param {HtmlEditor} this
26767              * @param {String} html
26768              */
26769             sync: true,
26770              /**
26771              * @event push
26772              * Fires when the iframe editor is updated with content from the textarea.
26773              * @param {HtmlEditor} this
26774              * @param {String} html
26775              */
26776             push: true,
26777              /**
26778              * @event editmodechange
26779              * Fires when the editor switches edit modes
26780              * @param {HtmlEditor} this
26781              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26782              */
26783             editmodechange: true,
26784             /**
26785              * @event editorevent
26786              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26787              * @param {HtmlEditor} this
26788              */
26789             editorevent: true,
26790             /**
26791              * @event firstfocus
26792              * Fires when on first focus - needed by toolbars..
26793              * @param {HtmlEditor} this
26794              */
26795             firstfocus: true,
26796             /**
26797              * @event autosave
26798              * Auto save the htmlEditor value as a file into Events
26799              * @param {HtmlEditor} this
26800              */
26801             autosave: true,
26802             /**
26803              * @event savedpreview
26804              * preview the saved version of htmlEditor
26805              * @param {HtmlEditor} this
26806              */
26807             savedpreview: true,
26808             
26809             /**
26810             * @event stylesheetsclick
26811             * Fires when press the Sytlesheets button
26812             * @param {Roo.HtmlEditorCore} this
26813             */
26814             stylesheetsclick: true,
26815             /**
26816             * @event paste
26817             * Fires when press user pastes into the editor
26818             * @param {Roo.HtmlEditorCore} this
26819             */
26820             paste: true 
26821         });
26822         this.defaultAutoCreate =  {
26823             tag: "textarea",
26824             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26825             autocomplete: "new-password"
26826         };
26827     },
26828
26829     /**
26830      * Protected method that will not generally be called directly. It
26831      * is called when the editor creates its toolbar. Override this method if you need to
26832      * add custom toolbar buttons.
26833      * @param {HtmlEditor} editor
26834      */
26835     createToolbar : function(editor){
26836         Roo.log("create toolbars");
26837         if (!editor.toolbars || !editor.toolbars.length) {
26838             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26839         }
26840         
26841         for (var i =0 ; i < editor.toolbars.length;i++) {
26842             editor.toolbars[i] = Roo.factory(
26843                     typeof(editor.toolbars[i]) == 'string' ?
26844                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26845                 Roo.form.HtmlEditor);
26846             editor.toolbars[i].init(editor);
26847         }
26848          
26849         
26850     },
26851     /**
26852      * get the Context selected node
26853      * @returns {DomElement|boolean} selected node if active or false if none
26854      * 
26855      */
26856     getSelectedNode : function()
26857     {
26858         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
26859             return false;
26860         }
26861         return this.toolbars[1].tb.selectedNode;
26862     
26863     },
26864     // private
26865     onRender : function(ct, position)
26866     {
26867         var _t = this;
26868         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26869         
26870         this.wrap = this.el.wrap({
26871             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26872         });
26873         
26874         this.editorcore.onRender(ct, position);
26875          
26876         if (this.resizable) {
26877             this.resizeEl = new Roo.Resizable(this.wrap, {
26878                 pinned : true,
26879                 wrap: true,
26880                 dynamic : true,
26881                 minHeight : this.height,
26882                 height: this.height,
26883                 handles : this.resizable,
26884                 width: this.width,
26885                 listeners : {
26886                     resize : function(r, w, h) {
26887                         _t.onResize(w,h); // -something
26888                     }
26889                 }
26890             });
26891             
26892         }
26893         this.createToolbar(this);
26894        
26895         
26896         if(!this.width){
26897             this.setSize(this.wrap.getSize());
26898         }
26899         if (this.resizeEl) {
26900             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26901             // should trigger onReize..
26902         }
26903         
26904         this.keyNav = new Roo.KeyNav(this.el, {
26905             
26906             "tab" : function(e){
26907                 e.preventDefault();
26908                 
26909                 var value = this.getValue();
26910                 
26911                 var start = this.el.dom.selectionStart;
26912                 var end = this.el.dom.selectionEnd;
26913                 
26914                 if(!e.shiftKey){
26915                     
26916                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26917                     this.el.dom.setSelectionRange(end + 1, end + 1);
26918                     return;
26919                 }
26920                 
26921                 var f = value.substring(0, start).split("\t");
26922                 
26923                 if(f.pop().length != 0){
26924                     return;
26925                 }
26926                 
26927                 this.setValue(f.join("\t") + value.substring(end));
26928                 this.el.dom.setSelectionRange(start - 1, start - 1);
26929                 
26930             },
26931             
26932             "home" : 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(0, 0);
26944                     return;
26945                 }
26946                 
26947                 var pos = 0;
26948                 
26949                 for (var i = 0; i < lines.length;i++) {
26950                     pos += lines[i].length;
26951                     
26952                     if(i != 0){
26953                         pos += 1;
26954                     }
26955                     
26956                     if(pos < curr){
26957                         continue;
26958                     }
26959                     
26960                     pos -= lines[i].length;
26961                     
26962                     break;
26963                 }
26964                 
26965                 if(!e.shiftKey){
26966                     this.el.dom.setSelectionRange(pos, pos);
26967                     return;
26968                 }
26969                 
26970                 this.el.dom.selectionStart = pos;
26971                 this.el.dom.selectionEnd = curr;
26972             },
26973             
26974             "end" : function(e){
26975                 e.preventDefault();
26976                 
26977                 var curr = this.el.dom.selectionStart;
26978                 var lines = this.getValue().split("\n");
26979                 
26980                 if(!lines.length){
26981                     return;
26982                 }
26983                 
26984                 if(e.ctrlKey){
26985                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26986                     return;
26987                 }
26988                 
26989                 var pos = 0;
26990                 
26991                 for (var i = 0; i < lines.length;i++) {
26992                     
26993                     pos += lines[i].length;
26994                     
26995                     if(i != 0){
26996                         pos += 1;
26997                     }
26998                     
26999                     if(pos < curr){
27000                         continue;
27001                     }
27002                     
27003                     break;
27004                 }
27005                 
27006                 if(!e.shiftKey){
27007                     this.el.dom.setSelectionRange(pos, pos);
27008                     return;
27009                 }
27010                 
27011                 this.el.dom.selectionStart = curr;
27012                 this.el.dom.selectionEnd = pos;
27013             },
27014
27015             scope : this,
27016
27017             doRelay : function(foo, bar, hname){
27018                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
27019             },
27020
27021             forceKeyDown: true
27022         });
27023         
27024 //        if(this.autosave && this.w){
27025 //            this.autoSaveFn = setInterval(this.autosave, 1000);
27026 //        }
27027     },
27028
27029     // private
27030     onResize : function(w, h)
27031     {
27032         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27033         var ew = false;
27034         var eh = false;
27035         
27036         if(this.el ){
27037             if(typeof w == 'number'){
27038                 var aw = w - this.wrap.getFrameWidth('lr');
27039                 this.el.setWidth(this.adjustWidth('textarea', aw));
27040                 ew = aw;
27041             }
27042             if(typeof h == 'number'){
27043                 var tbh = 0;
27044                 for (var i =0; i < this.toolbars.length;i++) {
27045                     // fixme - ask toolbars for heights?
27046                     tbh += this.toolbars[i].tb.el.getHeight();
27047                     if (this.toolbars[i].footer) {
27048                         tbh += this.toolbars[i].footer.el.getHeight();
27049                     }
27050                 }
27051                 
27052                 
27053                 
27054                 
27055                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27056                 ah -= 5; // knock a few pixes off for look..
27057 //                Roo.log(ah);
27058                 this.el.setHeight(this.adjustWidth('textarea', ah));
27059                 var eh = ah;
27060             }
27061         }
27062         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27063         this.editorcore.onResize(ew,eh);
27064         
27065     },
27066
27067     /**
27068      * Toggles the editor between standard and source edit mode.
27069      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27070      */
27071     toggleSourceEdit : function(sourceEditMode)
27072     {
27073         this.editorcore.toggleSourceEdit(sourceEditMode);
27074         
27075         if(this.editorcore.sourceEditMode){
27076             Roo.log('editor - showing textarea');
27077             
27078 //            Roo.log('in');
27079 //            Roo.log(this.syncValue());
27080             this.editorcore.syncValue();
27081             this.el.removeClass('x-hidden');
27082             this.el.dom.removeAttribute('tabIndex');
27083             this.el.focus();
27084             this.el.dom.scrollTop = 0;
27085             
27086             
27087             for (var i = 0; i < this.toolbars.length; i++) {
27088                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27089                     this.toolbars[i].tb.hide();
27090                     this.toolbars[i].footer.hide();
27091                 }
27092             }
27093             
27094         }else{
27095             Roo.log('editor - hiding textarea');
27096 //            Roo.log('out')
27097 //            Roo.log(this.pushValue()); 
27098             this.editorcore.pushValue();
27099             
27100             this.el.addClass('x-hidden');
27101             this.el.dom.setAttribute('tabIndex', -1);
27102             
27103             for (var i = 0; i < this.toolbars.length; i++) {
27104                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27105                     this.toolbars[i].tb.show();
27106                     this.toolbars[i].footer.show();
27107                 }
27108             }
27109             
27110             //this.deferFocus();
27111         }
27112         
27113         this.setSize(this.wrap.getSize());
27114         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27115         
27116         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27117     },
27118  
27119     // private (for BoxComponent)
27120     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27121
27122     // private (for BoxComponent)
27123     getResizeEl : function(){
27124         return this.wrap;
27125     },
27126
27127     // private (for BoxComponent)
27128     getPositionEl : function(){
27129         return this.wrap;
27130     },
27131
27132     // private
27133     initEvents : function(){
27134         this.originalValue = this.getValue();
27135     },
27136
27137     /**
27138      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27139      * @method
27140      */
27141     markInvalid : Roo.emptyFn,
27142     /**
27143      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27144      * @method
27145      */
27146     clearInvalid : Roo.emptyFn,
27147
27148     setValue : function(v){
27149         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27150         this.editorcore.pushValue();
27151     },
27152
27153     /**
27154      * update the language in the body - really done by core
27155      * @param {String} language - eg. en / ar / zh-CN etc..
27156      */
27157     updateLanguage : function(lang)
27158     {
27159         this.language = lang;
27160         this.editorcore.language = lang;
27161         this.editorcore.updateLanguage();
27162      
27163     },
27164     // private
27165     deferFocus : function(){
27166         this.focus.defer(10, this);
27167     },
27168
27169     // doc'ed in Field
27170     focus : function(){
27171         this.editorcore.focus();
27172         
27173     },
27174       
27175
27176     // private
27177     onDestroy : function(){
27178         
27179         
27180         
27181         if(this.rendered){
27182             
27183             for (var i =0; i < this.toolbars.length;i++) {
27184                 // fixme - ask toolbars for heights?
27185                 this.toolbars[i].onDestroy();
27186             }
27187             
27188             this.wrap.dom.innerHTML = '';
27189             this.wrap.remove();
27190         }
27191     },
27192
27193     // private
27194     onFirstFocus : function(){
27195         //Roo.log("onFirstFocus");
27196         this.editorcore.onFirstFocus();
27197          for (var i =0; i < this.toolbars.length;i++) {
27198             this.toolbars[i].onFirstFocus();
27199         }
27200         
27201     },
27202     
27203     // private
27204     syncValue : function()
27205     {
27206         this.editorcore.syncValue();
27207     },
27208     
27209     pushValue : function()
27210     {
27211         this.editorcore.pushValue();
27212     },
27213     
27214     setStylesheets : function(stylesheets)
27215     {
27216         this.editorcore.setStylesheets(stylesheets);
27217     },
27218     
27219     removeStylesheets : function()
27220     {
27221         this.editorcore.removeStylesheets();
27222     }
27223      
27224     
27225     // hide stuff that is not compatible
27226     /**
27227      * @event blur
27228      * @hide
27229      */
27230     /**
27231      * @event change
27232      * @hide
27233      */
27234     /**
27235      * @event focus
27236      * @hide
27237      */
27238     /**
27239      * @event specialkey
27240      * @hide
27241      */
27242     /**
27243      * @cfg {String} fieldClass @hide
27244      */
27245     /**
27246      * @cfg {String} focusClass @hide
27247      */
27248     /**
27249      * @cfg {String} autoCreate @hide
27250      */
27251     /**
27252      * @cfg {String} inputType @hide
27253      */
27254     /**
27255      * @cfg {String} invalidClass @hide
27256      */
27257     /**
27258      * @cfg {String} invalidText @hide
27259      */
27260     /**
27261      * @cfg {String} msgFx @hide
27262      */
27263     /**
27264      * @cfg {String} validateOnBlur @hide
27265      */
27266 });
27267  
27268     /*
27269  * Based on
27270  * Ext JS Library 1.1.1
27271  * Copyright(c) 2006-2007, Ext JS, LLC.
27272  *  
27273  
27274  */
27275
27276 /**
27277  * @class Roo.form.HtmlEditor.ToolbarStandard
27278  * Basic Toolbar
27279
27280  * Usage:
27281  *
27282  new Roo.form.HtmlEditor({
27283     ....
27284     toolbars : [
27285         new Roo.form.HtmlEditorToolbar1({
27286             disable : { fonts: 1 , format: 1, ..., ... , ...],
27287             btns : [ .... ]
27288         })
27289     }
27290      
27291  * 
27292  * @cfg {Object} disable List of elements to disable..
27293  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27294  * 
27295  * 
27296  * NEEDS Extra CSS? 
27297  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27298  */
27299  
27300 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27301 {
27302     
27303     Roo.apply(this, config);
27304     
27305     // default disabled, based on 'good practice'..
27306     this.disable = this.disable || {};
27307     Roo.applyIf(this.disable, {
27308         fontSize : true,
27309         colors : true,
27310         specialElements : true
27311     });
27312     
27313     
27314     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27315     // dont call parent... till later.
27316 }
27317
27318 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27319     
27320     tb: false,
27321     
27322     rendered: false,
27323     
27324     editor : false,
27325     editorcore : false,
27326     /**
27327      * @cfg {Object} disable  List of toolbar elements to disable
27328          
27329      */
27330     disable : false,
27331     
27332     
27333      /**
27334      * @cfg {String} createLinkText The default text for the create link prompt
27335      */
27336     createLinkText : 'Please enter the URL for the link:',
27337     /**
27338      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27339      */
27340     defaultLinkValue : 'http:/'+'/',
27341    
27342     
27343       /**
27344      * @cfg {Array} fontFamilies An array of available font families
27345      */
27346     fontFamilies : [
27347         'Arial',
27348         'Courier New',
27349         'Tahoma',
27350         'Times New Roman',
27351         'Verdana'
27352     ],
27353     
27354     specialChars : [
27355            "&#169;",
27356           "&#174;",     
27357           "&#8482;",    
27358           "&#163;" ,    
27359          // "&#8212;",    
27360           "&#8230;",    
27361           "&#247;" ,    
27362         //  "&#225;" ,     ?? a acute?
27363            "&#8364;"    , //Euro
27364        //   "&#8220;"    ,
27365         //  "&#8221;"    ,
27366         //  "&#8226;"    ,
27367           "&#176;"  //   , // degrees
27368
27369          // "&#233;"     , // e ecute
27370          // "&#250;"     , // u ecute?
27371     ],
27372     
27373     specialElements : [
27374         {
27375             text: "Insert Table",
27376             xtype: 'MenuItem',
27377             xns : Roo.Menu,
27378             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27379                 
27380         },
27381         {    
27382             text: "Insert Image",
27383             xtype: 'MenuItem',
27384             xns : Roo.Menu,
27385             ihtml : '<img src="about:blank"/>'
27386             
27387         }
27388         
27389          
27390     ],
27391     
27392     
27393     inputElements : [ 
27394             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27395             "input:submit", "input:button", "select", "textarea", "label" ],
27396     formats : [
27397         ["p"] ,  
27398         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27399         ["pre"],[ "code"], 
27400         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27401         ['div'],['span'],
27402         ['sup'],['sub']
27403     ],
27404     
27405     cleanStyles : [
27406         "font-size"
27407     ],
27408      /**
27409      * @cfg {String} defaultFont default font to use.
27410      */
27411     defaultFont: 'tahoma',
27412    
27413     fontSelect : false,
27414     
27415     
27416     formatCombo : false,
27417     
27418     init : function(editor)
27419     {
27420         this.editor = editor;
27421         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27422         var editorcore = this.editorcore;
27423         
27424         var _t = this;
27425         
27426         var fid = editorcore.frameId;
27427         var etb = this;
27428         function btn(id, toggle, handler){
27429             var xid = fid + '-'+ id ;
27430             return {
27431                 id : xid,
27432                 cmd : id,
27433                 cls : 'x-btn-icon x-edit-'+id,
27434                 enableToggle:toggle !== false,
27435                 scope: _t, // was editor...
27436                 handler:handler||_t.relayBtnCmd,
27437                 clickEvent:'mousedown',
27438                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27439                 tabIndex:-1
27440             };
27441         }
27442         
27443         
27444         
27445         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27446         this.tb = tb;
27447          // stop form submits
27448         tb.el.on('click', function(e){
27449             e.preventDefault(); // what does this do?
27450         });
27451
27452         if(!this.disable.font) { // && !Roo.isSafari){
27453             /* why no safari for fonts 
27454             editor.fontSelect = tb.el.createChild({
27455                 tag:'select',
27456                 tabIndex: -1,
27457                 cls:'x-font-select',
27458                 html: this.createFontOptions()
27459             });
27460             
27461             editor.fontSelect.on('change', function(){
27462                 var font = editor.fontSelect.dom.value;
27463                 editor.relayCmd('fontname', font);
27464                 editor.deferFocus();
27465             }, editor);
27466             
27467             tb.add(
27468                 editor.fontSelect.dom,
27469                 '-'
27470             );
27471             */
27472             
27473         };
27474         if(!this.disable.formats){
27475             this.formatCombo = new Roo.form.ComboBox({
27476                 store: new Roo.data.SimpleStore({
27477                     id : 'tag',
27478                     fields: ['tag'],
27479                     data : this.formats // from states.js
27480                 }),
27481                 blockFocus : true,
27482                 name : '',
27483                 //autoCreate : {tag: "div",  size: "20"},
27484                 displayField:'tag',
27485                 typeAhead: false,
27486                 mode: 'local',
27487                 editable : false,
27488                 triggerAction: 'all',
27489                 emptyText:'Add tag',
27490                 selectOnFocus:true,
27491                 width:135,
27492                 listeners : {
27493                     'select': function(c, r, i) {
27494                         editorcore.insertTag(r.get('tag'));
27495                         editor.focus();
27496                     }
27497                 }
27498
27499             });
27500             tb.addField(this.formatCombo);
27501             
27502         }
27503         
27504         if(!this.disable.format){
27505             tb.add(
27506                 btn('bold'),
27507                 btn('italic'),
27508                 btn('underline'),
27509                 btn('strikethrough')
27510             );
27511         };
27512         if(!this.disable.fontSize){
27513             tb.add(
27514                 '-',
27515                 
27516                 
27517                 btn('increasefontsize', false, editorcore.adjustFont),
27518                 btn('decreasefontsize', false, editorcore.adjustFont)
27519             );
27520         };
27521         
27522         
27523         if(!this.disable.colors){
27524             tb.add(
27525                 '-', {
27526                     id:editorcore.frameId +'-forecolor',
27527                     cls:'x-btn-icon x-edit-forecolor',
27528                     clickEvent:'mousedown',
27529                     tooltip: this.buttonTips['forecolor'] || undefined,
27530                     tabIndex:-1,
27531                     menu : new Roo.menu.ColorMenu({
27532                         allowReselect: true,
27533                         focus: Roo.emptyFn,
27534                         value:'000000',
27535                         plain:true,
27536                         selectHandler: function(cp, color){
27537                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27538                             editor.deferFocus();
27539                         },
27540                         scope: editorcore,
27541                         clickEvent:'mousedown'
27542                     })
27543                 }, {
27544                     id:editorcore.frameId +'backcolor',
27545                     cls:'x-btn-icon x-edit-backcolor',
27546                     clickEvent:'mousedown',
27547                     tooltip: this.buttonTips['backcolor'] || undefined,
27548                     tabIndex:-1,
27549                     menu : new Roo.menu.ColorMenu({
27550                         focus: Roo.emptyFn,
27551                         value:'FFFFFF',
27552                         plain:true,
27553                         allowReselect: true,
27554                         selectHandler: function(cp, color){
27555                             if(Roo.isGecko){
27556                                 editorcore.execCmd('useCSS', false);
27557                                 editorcore.execCmd('hilitecolor', color);
27558                                 editorcore.execCmd('useCSS', true);
27559                                 editor.deferFocus();
27560                             }else{
27561                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27562                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27563                                 editor.deferFocus();
27564                             }
27565                         },
27566                         scope:editorcore,
27567                         clickEvent:'mousedown'
27568                     })
27569                 }
27570             );
27571         };
27572         // now add all the items...
27573         
27574
27575         if(!this.disable.alignments){
27576             tb.add(
27577                 '-',
27578                 btn('justifyleft'),
27579                 btn('justifycenter'),
27580                 btn('justifyright')
27581             );
27582         };
27583
27584         //if(!Roo.isSafari){
27585             if(!this.disable.links){
27586                 tb.add(
27587                     '-',
27588                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27589                 );
27590             };
27591
27592             if(!this.disable.lists){
27593                 tb.add(
27594                     '-',
27595                     btn('insertorderedlist'),
27596                     btn('insertunorderedlist')
27597                 );
27598             }
27599             if(!this.disable.sourceEdit){
27600                 tb.add(
27601                     '-',
27602                     btn('sourceedit', true, function(btn){
27603                         this.toggleSourceEdit(btn.pressed);
27604                     })
27605                 );
27606             }
27607         //}
27608         
27609         var smenu = { };
27610         // special menu.. - needs to be tidied up..
27611         if (!this.disable.special) {
27612             smenu = {
27613                 text: "&#169;",
27614                 cls: 'x-edit-none',
27615                 
27616                 menu : {
27617                     items : []
27618                 }
27619             };
27620             for (var i =0; i < this.specialChars.length; i++) {
27621                 smenu.menu.items.push({
27622                     
27623                     html: this.specialChars[i],
27624                     handler: function(a,b) {
27625                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27626                         //editor.insertAtCursor(a.html);
27627                         
27628                     },
27629                     tabIndex:-1
27630                 });
27631             }
27632             
27633             
27634             tb.add(smenu);
27635             
27636             
27637         }
27638         
27639         var cmenu = { };
27640         if (!this.disable.cleanStyles) {
27641             cmenu = {
27642                 cls: 'x-btn-icon x-btn-clear',
27643                 
27644                 menu : {
27645                     items : []
27646                 }
27647             };
27648             for (var i =0; i < this.cleanStyles.length; i++) {
27649                 cmenu.menu.items.push({
27650                     actiontype : this.cleanStyles[i],
27651                     html: 'Remove ' + this.cleanStyles[i],
27652                     handler: function(a,b) {
27653 //                        Roo.log(a);
27654 //                        Roo.log(b);
27655                         var c = Roo.get(editorcore.doc.body);
27656                         c.select('[style]').each(function(s) {
27657                             s.dom.style.removeProperty(a.actiontype);
27658                         });
27659                         editorcore.syncValue();
27660                     },
27661                     tabIndex:-1
27662                 });
27663             }
27664             cmenu.menu.items.push({
27665                 actiontype : 'tablewidths',
27666                 html: 'Remove Table Widths',
27667                 handler: function(a,b) {
27668                     editorcore.cleanTableWidths();
27669                     editorcore.syncValue();
27670                 },
27671                 tabIndex:-1
27672             });
27673             cmenu.menu.items.push({
27674                 actiontype : 'word',
27675                 html: 'Remove MS Word Formating',
27676                 handler: function(a,b) {
27677                     editorcore.cleanWord();
27678                     editorcore.syncValue();
27679                 },
27680                 tabIndex:-1
27681             });
27682             
27683             cmenu.menu.items.push({
27684                 actiontype : 'all',
27685                 html: 'Remove All Styles',
27686                 handler: function(a,b) {
27687                     
27688                     var c = Roo.get(editorcore.doc.body);
27689                     c.select('[style]').each(function(s) {
27690                         s.dom.removeAttribute('style');
27691                     });
27692                     editorcore.syncValue();
27693                 },
27694                 tabIndex:-1
27695             });
27696             
27697             cmenu.menu.items.push({
27698                 actiontype : 'all',
27699                 html: 'Remove All CSS Classes',
27700                 handler: function(a,b) {
27701                     
27702                     var c = Roo.get(editorcore.doc.body);
27703                     c.select('[class]').each(function(s) {
27704                         s.dom.removeAttribute('class');
27705                     });
27706                     editorcore.cleanWord();
27707                     editorcore.syncValue();
27708                 },
27709                 tabIndex:-1
27710             });
27711             
27712              cmenu.menu.items.push({
27713                 actiontype : 'tidy',
27714                 html: 'Tidy HTML Source',
27715                 handler: function(a,b) {
27716                     new Roo.htmleditor.Tidy(editorcore.doc.body);
27717                     editorcore.syncValue();
27718                 },
27719                 tabIndex:-1
27720             });
27721             
27722             
27723             tb.add(cmenu);
27724         }
27725          
27726         if (!this.disable.specialElements) {
27727             var semenu = {
27728                 text: "Other;",
27729                 cls: 'x-edit-none',
27730                 menu : {
27731                     items : []
27732                 }
27733             };
27734             for (var i =0; i < this.specialElements.length; i++) {
27735                 semenu.menu.items.push(
27736                     Roo.apply({ 
27737                         handler: function(a,b) {
27738                             editor.insertAtCursor(this.ihtml);
27739                         }
27740                     }, this.specialElements[i])
27741                 );
27742                     
27743             }
27744             
27745             tb.add(semenu);
27746             
27747             
27748         }
27749          
27750         
27751         if (this.btns) {
27752             for(var i =0; i< this.btns.length;i++) {
27753                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27754                 b.cls =  'x-edit-none';
27755                 
27756                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27757                     b.cls += ' x-init-enable';
27758                 }
27759                 
27760                 b.scope = editorcore;
27761                 tb.add(b);
27762             }
27763         
27764         }
27765         
27766         
27767         
27768         // disable everything...
27769         
27770         this.tb.items.each(function(item){
27771             
27772            if(
27773                 item.id != editorcore.frameId+ '-sourceedit' && 
27774                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27775             ){
27776                 
27777                 item.disable();
27778             }
27779         });
27780         this.rendered = true;
27781         
27782         // the all the btns;
27783         editor.on('editorevent', this.updateToolbar, this);
27784         // other toolbars need to implement this..
27785         //editor.on('editmodechange', this.updateToolbar, this);
27786     },
27787     
27788     
27789     relayBtnCmd : function(btn) {
27790         this.editorcore.relayCmd(btn.cmd);
27791     },
27792     // private used internally
27793     createLink : function(){
27794         //Roo.log("create link?");
27795         var ec = this.editorcore;
27796         var ar = ec.getAllAncestors();
27797         var n = false;
27798         for(var i = 0;i< ar.length;i++) {
27799             if (ar[i] && ar[i].nodeName == 'A') {
27800                 n = ar[i];
27801                 break;
27802             }
27803         }
27804         
27805         (function() {
27806             
27807             Roo.MessageBox.show({
27808                 title : "Add / Edit Link URL",
27809                 msg : "Enter the url for the link",
27810                 buttons: Roo.MessageBox.OKCANCEL,
27811                 fn: function(btn, url){
27812                     if (btn != 'ok') {
27813                         return;
27814                     }
27815                     if(url && url != 'http:/'+'/'){
27816                         if (n) {
27817                             n.setAttribute('href', url);
27818                         } else {
27819                             ec.relayCmd('createlink', url);
27820                         }
27821                     }
27822                 },
27823                 minWidth:250,
27824                 prompt:true,
27825                 //multiline: multiline,
27826                 modal : true,
27827                 value :  n  ? n.getAttribute('href') : '' 
27828             });
27829             
27830              
27831         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
27832         
27833     },
27834
27835     
27836     /**
27837      * Protected method that will not generally be called directly. It triggers
27838      * a toolbar update by reading the markup state of the current selection in the editor.
27839      */
27840     updateToolbar: function(){
27841
27842         if(!this.editorcore.activated){
27843             this.editor.onFirstFocus();
27844             return;
27845         }
27846
27847         var btns = this.tb.items.map, 
27848             doc = this.editorcore.doc,
27849             frameId = this.editorcore.frameId;
27850
27851         if(!this.disable.font && !Roo.isSafari){
27852             /*
27853             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27854             if(name != this.fontSelect.dom.value){
27855                 this.fontSelect.dom.value = name;
27856             }
27857             */
27858         }
27859         if(!this.disable.format){
27860             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27861             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27862             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27863             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27864         }
27865         if(!this.disable.alignments){
27866             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27867             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27868             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27869         }
27870         if(!Roo.isSafari && !this.disable.lists){
27871             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27872             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27873         }
27874         
27875         var ans = this.editorcore.getAllAncestors();
27876         if (this.formatCombo) {
27877             
27878             
27879             var store = this.formatCombo.store;
27880             this.formatCombo.setValue("");
27881             for (var i =0; i < ans.length;i++) {
27882                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27883                     // select it..
27884                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27885                     break;
27886                 }
27887             }
27888         }
27889         
27890         
27891         
27892         // hides menus... - so this cant be on a menu...
27893         Roo.menu.MenuMgr.hideAll();
27894
27895         //this.editorsyncValue();
27896     },
27897    
27898     
27899     createFontOptions : function(){
27900         var buf = [], fs = this.fontFamilies, ff, lc;
27901         
27902         
27903         
27904         for(var i = 0, len = fs.length; i< len; i++){
27905             ff = fs[i];
27906             lc = ff.toLowerCase();
27907             buf.push(
27908                 '<option value="',lc,'" style="font-family:',ff,';"',
27909                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27910                     ff,
27911                 '</option>'
27912             );
27913         }
27914         return buf.join('');
27915     },
27916     
27917     toggleSourceEdit : function(sourceEditMode){
27918         
27919         Roo.log("toolbar toogle");
27920         if(sourceEditMode === undefined){
27921             sourceEditMode = !this.sourceEditMode;
27922         }
27923         this.sourceEditMode = sourceEditMode === true;
27924         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27925         // just toggle the button?
27926         if(btn.pressed !== this.sourceEditMode){
27927             btn.toggle(this.sourceEditMode);
27928             return;
27929         }
27930         
27931         if(sourceEditMode){
27932             Roo.log("disabling buttons");
27933             this.tb.items.each(function(item){
27934                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27935                     item.disable();
27936                 }
27937             });
27938           
27939         }else{
27940             Roo.log("enabling buttons");
27941             if(this.editorcore.initialized){
27942                 this.tb.items.each(function(item){
27943                     item.enable();
27944                 });
27945                 // initialize 'blocks'
27946                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
27947                     Roo.htmleditor.Block.factory(e).updateElement(e);
27948                 },this);
27949             
27950             }
27951             
27952         }
27953         Roo.log("calling toggole on editor");
27954         // tell the editor that it's been pressed..
27955         this.editor.toggleSourceEdit(sourceEditMode);
27956        
27957     },
27958      /**
27959      * Object collection of toolbar tooltips for the buttons in the editor. The key
27960      * is the command id associated with that button and the value is a valid QuickTips object.
27961      * For example:
27962 <pre><code>
27963 {
27964     bold : {
27965         title: 'Bold (Ctrl+B)',
27966         text: 'Make the selected text bold.',
27967         cls: 'x-html-editor-tip'
27968     },
27969     italic : {
27970         title: 'Italic (Ctrl+I)',
27971         text: 'Make the selected text italic.',
27972         cls: 'x-html-editor-tip'
27973     },
27974     ...
27975 </code></pre>
27976     * @type Object
27977      */
27978     buttonTips : {
27979         bold : {
27980             title: 'Bold (Ctrl+B)',
27981             text: 'Make the selected text bold.',
27982             cls: 'x-html-editor-tip'
27983         },
27984         italic : {
27985             title: 'Italic (Ctrl+I)',
27986             text: 'Make the selected text italic.',
27987             cls: 'x-html-editor-tip'
27988         },
27989         underline : {
27990             title: 'Underline (Ctrl+U)',
27991             text: 'Underline the selected text.',
27992             cls: 'x-html-editor-tip'
27993         },
27994         strikethrough : {
27995             title: 'Strikethrough',
27996             text: 'Strikethrough the selected text.',
27997             cls: 'x-html-editor-tip'
27998         },
27999         increasefontsize : {
28000             title: 'Grow Text',
28001             text: 'Increase the font size.',
28002             cls: 'x-html-editor-tip'
28003         },
28004         decreasefontsize : {
28005             title: 'Shrink Text',
28006             text: 'Decrease the font size.',
28007             cls: 'x-html-editor-tip'
28008         },
28009         backcolor : {
28010             title: 'Text Highlight Color',
28011             text: 'Change the background color of the selected text.',
28012             cls: 'x-html-editor-tip'
28013         },
28014         forecolor : {
28015             title: 'Font Color',
28016             text: 'Change the color of the selected text.',
28017             cls: 'x-html-editor-tip'
28018         },
28019         justifyleft : {
28020             title: 'Align Text Left',
28021             text: 'Align text to the left.',
28022             cls: 'x-html-editor-tip'
28023         },
28024         justifycenter : {
28025             title: 'Center Text',
28026             text: 'Center text in the editor.',
28027             cls: 'x-html-editor-tip'
28028         },
28029         justifyright : {
28030             title: 'Align Text Right',
28031             text: 'Align text to the right.',
28032             cls: 'x-html-editor-tip'
28033         },
28034         insertunorderedlist : {
28035             title: 'Bullet List',
28036             text: 'Start a bulleted list.',
28037             cls: 'x-html-editor-tip'
28038         },
28039         insertorderedlist : {
28040             title: 'Numbered List',
28041             text: 'Start a numbered list.',
28042             cls: 'x-html-editor-tip'
28043         },
28044         createlink : {
28045             title: 'Hyperlink',
28046             text: 'Make the selected text a hyperlink.',
28047             cls: 'x-html-editor-tip'
28048         },
28049         sourceedit : {
28050             title: 'Source Edit',
28051             text: 'Switch to source editing mode.',
28052             cls: 'x-html-editor-tip'
28053         }
28054     },
28055     // private
28056     onDestroy : function(){
28057         if(this.rendered){
28058             
28059             this.tb.items.each(function(item){
28060                 if(item.menu){
28061                     item.menu.removeAll();
28062                     if(item.menu.el){
28063                         item.menu.el.destroy();
28064                     }
28065                 }
28066                 item.destroy();
28067             });
28068              
28069         }
28070     },
28071     onFirstFocus: function() {
28072         this.tb.items.each(function(item){
28073            item.enable();
28074         });
28075     }
28076 };
28077
28078
28079
28080
28081 // <script type="text/javascript">
28082 /*
28083  * Based on
28084  * Ext JS Library 1.1.1
28085  * Copyright(c) 2006-2007, Ext JS, LLC.
28086  *  
28087  
28088  */
28089
28090  
28091 /**
28092  * @class Roo.form.HtmlEditor.ToolbarContext
28093  * Context Toolbar
28094  * 
28095  * Usage:
28096  *
28097  new Roo.form.HtmlEditor({
28098     ....
28099     toolbars : [
28100         { xtype: 'ToolbarStandard', styles : {} }
28101         { xtype: 'ToolbarContext', disable : {} }
28102     ]
28103 })
28104
28105      
28106  * 
28107  * @config : {Object} disable List of elements to disable.. (not done yet.)
28108  * @config : {Object} styles  Map of styles available.
28109  * 
28110  */
28111
28112 Roo.form.HtmlEditor.ToolbarContext = function(config)
28113 {
28114     
28115     Roo.apply(this, config);
28116     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28117     // dont call parent... till later.
28118     this.styles = this.styles || {};
28119 }
28120
28121  
28122
28123 Roo.form.HtmlEditor.ToolbarContext.types = {
28124     'IMG' : [
28125         {
28126             name : 'width',
28127             title: "Width",
28128             width: 40
28129         },
28130         {
28131             name : 'height',
28132             title: "Height",
28133             width: 40
28134         },
28135         {
28136             name : 'align',
28137             title: "Align",
28138             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28139             width : 80
28140             
28141         },
28142         {
28143             name : 'border',
28144             title: "Border",
28145             width: 40
28146         },
28147         {
28148             name : 'alt',
28149             title: "Alt",
28150             width: 120
28151         },
28152         {
28153             name : 'src',
28154             title: "Src",
28155             width: 220
28156         }
28157         
28158     ],
28159     
28160     'FIGURE' : [
28161         {
28162             name : 'align',
28163             title: "Align",
28164             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28165             width : 80  
28166         }
28167     ],
28168     'A' : [
28169         {
28170             name : 'name',
28171             title: "Name",
28172             width: 50
28173         },
28174         {
28175             name : 'target',
28176             title: "Target",
28177             width: 120
28178         },
28179         {
28180             name : 'href',
28181             title: "Href",
28182             width: 220
28183         } // border?
28184         
28185     ],
28186     
28187     'INPUT' : [
28188         {
28189             name : 'name',
28190             title: "name",
28191             width: 120
28192         },
28193         {
28194             name : 'value',
28195             title: "Value",
28196             width: 120
28197         },
28198         {
28199             name : 'width',
28200             title: "Width",
28201             width: 40
28202         }
28203     ],
28204     'LABEL' : [
28205          {
28206             name : 'for',
28207             title: "For",
28208             width: 120
28209         }
28210     ],
28211     'TEXTAREA' : [
28212         {
28213             name : 'name',
28214             title: "name",
28215             width: 120
28216         },
28217         {
28218             name : 'rows',
28219             title: "Rows",
28220             width: 20
28221         },
28222         {
28223             name : 'cols',
28224             title: "Cols",
28225             width: 20
28226         }
28227     ],
28228     'SELECT' : [
28229         {
28230             name : 'name',
28231             title: "name",
28232             width: 120
28233         },
28234         {
28235             name : 'selectoptions',
28236             title: "Options",
28237             width: 200
28238         }
28239     ],
28240     
28241     // should we really allow this??
28242     // should this just be 
28243     'BODY' : [
28244         
28245         {
28246             name : 'title',
28247             title: "Title",
28248             width: 200,
28249             disabled : true
28250         }
28251     ],
28252  
28253     '*' : [
28254         // empty.
28255     ]
28256
28257 };
28258
28259 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28260 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28261
28262 Roo.form.HtmlEditor.ToolbarContext.options = {
28263         'font-family'  : [ 
28264                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28265                 [ 'Courier New', 'Courier New'],
28266                 [ 'Tahoma', 'Tahoma'],
28267                 [ 'Times New Roman,serif', 'Times'],
28268                 [ 'Verdana','Verdana' ]
28269         ]
28270 };
28271
28272 // fixme - these need to be configurable..
28273  
28274
28275 //Roo.form.HtmlEditor.ToolbarContext.types
28276
28277
28278 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28279     
28280     tb: false,
28281     
28282     rendered: false,
28283     
28284     editor : false,
28285     editorcore : false,
28286     /**
28287      * @cfg {Object} disable  List of toolbar elements to disable
28288          
28289      */
28290     disable : false,
28291     /**
28292      * @cfg {Object} styles List of styles 
28293      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28294      *
28295      * These must be defined in the page, so they get rendered correctly..
28296      * .headline { }
28297      * TD.underline { }
28298      * 
28299      */
28300     styles : false,
28301     
28302     options: false,
28303     
28304     toolbars : false,
28305     
28306     init : function(editor)
28307     {
28308         this.editor = editor;
28309         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28310         var editorcore = this.editorcore;
28311         
28312         var fid = editorcore.frameId;
28313         var etb = this;
28314         function btn(id, toggle, handler){
28315             var xid = fid + '-'+ id ;
28316             return {
28317                 id : xid,
28318                 cmd : id,
28319                 cls : 'x-btn-icon x-edit-'+id,
28320                 enableToggle:toggle !== false,
28321                 scope: editorcore, // was editor...
28322                 handler:handler||editorcore.relayBtnCmd,
28323                 clickEvent:'mousedown',
28324                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28325                 tabIndex:-1
28326             };
28327         }
28328         // create a new element.
28329         var wdiv = editor.wrap.createChild({
28330                 tag: 'div'
28331             }, editor.wrap.dom.firstChild.nextSibling, true);
28332         
28333         // can we do this more than once??
28334         
28335          // stop form submits
28336       
28337  
28338         // disable everything...
28339         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28340         this.toolbars = {};
28341         // block toolbars are built in updateToolbar when needed.
28342         for (var i in  ty) {
28343             
28344             this.toolbars[i] = this.buildToolbar(ty[i],i);
28345         }
28346         this.tb = this.toolbars.BODY;
28347         this.tb.el.show();
28348         this.buildFooter();
28349         this.footer.show();
28350         editor.on('hide', function( ) { this.footer.hide() }, this);
28351         editor.on('show', function( ) { this.footer.show() }, this);
28352         
28353          
28354         this.rendered = true;
28355         
28356         // the all the btns;
28357         editor.on('editorevent', this.updateToolbar, this);
28358         // other toolbars need to implement this..
28359         //editor.on('editmodechange', this.updateToolbar, this);
28360     },
28361     
28362     
28363     
28364     /**
28365      * Protected method that will not generally be called directly. It triggers
28366      * a toolbar update by reading the markup state of the current selection in the editor.
28367      *
28368      * Note you can force an update by calling on('editorevent', scope, false)
28369      */
28370     updateToolbar: function(editor ,ev, sel)
28371     {
28372         
28373         if (ev) {
28374             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28375         }
28376         
28377         //Roo.log(ev);
28378         // capture mouse up - this is handy for selecting images..
28379         // perhaps should go somewhere else...
28380         if(!this.editorcore.activated){
28381              this.editor.onFirstFocus();
28382             return;
28383         }
28384         //Roo.log(ev ? ev.target : 'NOTARGET');
28385         
28386         
28387         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28388         // selectNode - might want to handle IE?
28389         
28390         
28391         
28392         if (ev &&
28393             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28394             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28395             // they have click on an image...
28396             // let's see if we can change the selection...
28397             sel = ev.target;
28398             
28399             // this triggers looping?
28400             //this.editorcore.selectNode(sel);
28401              
28402         }
28403         
28404         // this forces an id..
28405         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28406              e.classList.remove('roo-ed-selection');
28407         });
28408         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28409         //Roo.get(node).addClass('roo-ed-selection');
28410       
28411         //var updateFooter = sel ? false : true; 
28412         
28413         
28414         var ans = this.editorcore.getAllAncestors();
28415         
28416         // pick
28417         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28418         
28419         if (!sel) { 
28420             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28421             sel = sel ? sel : this.editorcore.doc.body;
28422             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28423             
28424         }
28425         
28426         var tn = sel.tagName.toUpperCase();
28427         var lastSel = this.tb.selectedNode;
28428         this.tb.selectedNode = sel;
28429         var left_label = tn;
28430         
28431         // ok see if we are editing a block?
28432         
28433         var db = false;
28434         // you are not actually selecting the block.
28435         if (sel && sel.hasAttribute('data-block')) {
28436             db = sel;
28437         } else if (sel && sel.closest('[data-block]')) {
28438             
28439             db = sel.closest('[data-block]');
28440             //var cepar = sel.closest('[contenteditable=true]');
28441             //if (db && cepar && cepar.tagName != 'BODY') {
28442             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28443             //}   
28444         }
28445         
28446         
28447         var block = false;
28448         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28449         if (db && this.editorcore.enableBlocks) {
28450             block = Roo.htmleditor.Block.factory(db);
28451             
28452             
28453             if (block) {
28454                  db.className = (
28455                         db.classList.length > 0  ? db.className + ' ' : ''
28456                     )  + 'roo-ed-selection';
28457                  
28458                  // since we removed it earlier... its not there..
28459                 tn = 'BLOCK.' + db.getAttribute('data-block');
28460                 
28461                 //this.editorcore.selectNode(db);
28462                 if (typeof(this.toolbars[tn]) == 'undefined') {
28463                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28464                 }
28465                 this.toolbars[tn].selectedNode = db;
28466                 left_label = block.friendly_name;
28467                 ans = this.editorcore.getAllAncestors();
28468             }
28469             
28470                 
28471             
28472         }
28473         
28474         
28475         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28476             return; // no change?
28477         }
28478         
28479         
28480           
28481         this.tb.el.hide();
28482         ///console.log("show: " + tn);
28483         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28484         
28485         this.tb.el.show();
28486         // update name
28487         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
28488         
28489         
28490         // update attributes
28491         if (block && this.tb.fields) {
28492              
28493             this.tb.fields.each(function(e) {
28494                 e.setValue(block[e.name]);
28495             });
28496             
28497             
28498         } else  if (this.tb.fields && this.tb.selectedNode) {
28499             this.tb.fields.each( function(e) {
28500                 if (e.stylename) {
28501                     e.setValue(this.tb.selectedNode.style[e.stylename]);
28502                     return;
28503                 } 
28504                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28505             }, this);
28506             this.updateToolbarStyles(this.tb.selectedNode);  
28507         }
28508         
28509         
28510        
28511         Roo.menu.MenuMgr.hideAll();
28512
28513         
28514         
28515     
28516         // update the footer
28517         //
28518         this.updateFooter(ans);
28519              
28520     },
28521     
28522     updateToolbarStyles : function(sel)
28523     {
28524         var hasStyles = false;
28525         for(var i in this.styles) {
28526             hasStyles = true;
28527             break;
28528         }
28529         
28530         // update styles
28531         if (hasStyles && this.tb.hasStyles) { 
28532             var st = this.tb.fields.item(0);
28533             
28534             st.store.removeAll();
28535             var cn = sel.className.split(/\s+/);
28536             
28537             var avs = [];
28538             if (this.styles['*']) {
28539                 
28540                 Roo.each(this.styles['*'], function(v) {
28541                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28542                 });
28543             }
28544             if (this.styles[tn]) { 
28545                 Roo.each(this.styles[tn], function(v) {
28546                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28547                 });
28548             }
28549             
28550             st.store.loadData(avs);
28551             st.collapse();
28552             st.setValue(cn);
28553         }
28554     },
28555     
28556      
28557     updateFooter : function(ans)
28558     {
28559         var html = '';
28560         if (ans === false) {
28561             this.footDisp.dom.innerHTML = '';
28562             return;
28563         }
28564         
28565         this.footerEls = ans.reverse();
28566         Roo.each(this.footerEls, function(a,i) {
28567             if (!a) { return; }
28568             html += html.length ? ' &gt; '  :  '';
28569             
28570             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28571             
28572         });
28573        
28574         // 
28575         var sz = this.footDisp.up('td').getSize();
28576         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28577         this.footDisp.dom.style.marginLeft = '5px';
28578         
28579         this.footDisp.dom.style.overflow = 'hidden';
28580         
28581         this.footDisp.dom.innerHTML = html;
28582             
28583         
28584     },
28585    
28586        
28587     // private
28588     onDestroy : function(){
28589         if(this.rendered){
28590             
28591             this.tb.items.each(function(item){
28592                 if(item.menu){
28593                     item.menu.removeAll();
28594                     if(item.menu.el){
28595                         item.menu.el.destroy();
28596                     }
28597                 }
28598                 item.destroy();
28599             });
28600              
28601         }
28602     },
28603     onFirstFocus: function() {
28604         // need to do this for all the toolbars..
28605         this.tb.items.each(function(item){
28606            item.enable();
28607         });
28608     },
28609     buildToolbar: function(tlist, nm, friendly_name, block)
28610     {
28611         var editor = this.editor;
28612         var editorcore = this.editorcore;
28613          // create a new element.
28614         var wdiv = editor.wrap.createChild({
28615                 tag: 'div'
28616             }, editor.wrap.dom.firstChild.nextSibling, true);
28617         
28618        
28619         var tb = new Roo.Toolbar(wdiv);
28620         ///this.tb = tb; // << this sets the active toolbar..
28621         if (tlist === false && block) {
28622             tlist = block.contextMenu(this);
28623         }
28624         
28625         tb.hasStyles = false;
28626         tb.name = nm;
28627         
28628         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
28629         
28630         var styles = Array.from(this.styles);
28631         
28632         
28633         // styles...
28634         if (styles && styles.length) {
28635             tb.hasStyles = true;
28636             // this needs a multi-select checkbox...
28637             tb.addField( new Roo.form.ComboBox({
28638                 store: new Roo.data.SimpleStore({
28639                     id : 'val',
28640                     fields: ['val', 'selected'],
28641                     data : [] 
28642                 }),
28643                 name : '-roo-edit-className',
28644                 attrname : 'className',
28645                 displayField: 'val',
28646                 typeAhead: false,
28647                 mode: 'local',
28648                 editable : false,
28649                 triggerAction: 'all',
28650                 emptyText:'Select Style',
28651                 selectOnFocus:true,
28652                 width: 130,
28653                 listeners : {
28654                     'select': function(c, r, i) {
28655                         // initial support only for on class per el..
28656                         tb.selectedNode.className =  r ? r.get('val') : '';
28657                         editorcore.syncValue();
28658                     }
28659                 }
28660     
28661             }));
28662         }
28663         
28664         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28665         
28666         
28667         for (var i = 0; i < tlist.length; i++) {
28668             
28669             // newer versions will use xtype cfg to create menus.
28670             if (typeof(tlist[i].xtype) != 'undefined') {
28671                 
28672                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28673                 
28674                 
28675                 continue;
28676             }
28677             
28678             var item = tlist[i];
28679             tb.add(item.title + ":&nbsp;");
28680             
28681             
28682             //optname == used so you can configure the options available..
28683             var opts = item.opts ? item.opts : false;
28684             if (item.optname) { // use the b
28685                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28686            
28687             }
28688             
28689             if (opts) {
28690                 // opts == pulldown..
28691                 tb.addField( new Roo.form.ComboBox({
28692                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28693                         id : 'val',
28694                         fields: ['val', 'display'],
28695                         data : opts  
28696                     }),
28697                     name : '-roo-edit-' + tlist[i].name,
28698                     
28699                     attrname : tlist[i].name,
28700                     stylename : item.style ? item.style : false,
28701                     
28702                     displayField: item.displayField ? item.displayField : 'val',
28703                     valueField :  'val',
28704                     typeAhead: false,
28705                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
28706                     editable : false,
28707                     triggerAction: 'all',
28708                     emptyText:'Select',
28709                     selectOnFocus:true,
28710                     width: item.width ? item.width  : 130,
28711                     listeners : {
28712                         'select': function(c, r, i) {
28713                              
28714                             
28715                             if (c.stylename) {
28716                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28717                                 editorcore.syncValue();
28718                                 return;
28719                             }
28720                             if (r === false) {
28721                                 tb.selectedNode.removeAttribute(c.attrname);
28722                                 editorcore.syncValue();
28723                                 return;
28724                             }
28725                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28726                             editorcore.syncValue();
28727                         }
28728                     }
28729
28730                 }));
28731                 continue;
28732                     
28733                  
28734                 /*
28735                 tb.addField( new Roo.form.TextField({
28736                     name: i,
28737                     width: 100,
28738                     //allowBlank:false,
28739                     value: ''
28740                 }));
28741                 continue;
28742                 */
28743             }
28744             tb.addField( new Roo.form.TextField({
28745                 name: '-roo-edit-' + tlist[i].name,
28746                 attrname : tlist[i].name,
28747                 
28748                 width: item.width,
28749                 //allowBlank:true,
28750                 value: '',
28751                 listeners: {
28752                     'change' : function(f, nv, ov) {
28753                         
28754                          
28755                         tb.selectedNode.setAttribute(f.attrname, nv);
28756                         editorcore.syncValue();
28757                     }
28758                 }
28759             }));
28760              
28761         }
28762         
28763         var _this = this;
28764         var show_delete = !block || block.deleteTitle !== false;
28765         if(nm == 'BODY'){
28766             show_delete = false;
28767             tb.addSeparator();
28768         
28769             tb.addButton( {
28770                 text: 'Stylesheets',
28771
28772                 listeners : {
28773                     click : function ()
28774                     {
28775                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28776                     }
28777                 }
28778             });
28779         }
28780         
28781         tb.addFill();
28782         if (show_delete) {
28783             tb.addButton({
28784                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28785         
28786                 listeners : {
28787                     click : function ()
28788                     {
28789                         var sn = tb.selectedNode;
28790                         if (block) {
28791                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28792                             
28793                         }
28794                         if (!sn) {
28795                             return;
28796                         }
28797                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28798                         if (sn.hasAttribute('data-block')) {
28799                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
28800                             sn.parentNode.removeChild(sn);
28801                             
28802                         } else if (sn && sn.tagName != 'BODY') {
28803                             // remove and keep parents.
28804                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28805                             a.replaceTag(sn);
28806                         }
28807                         
28808                         
28809                         var range = editorcore.createRange();
28810             
28811                         range.setStart(stn,0);
28812                         range.setEnd(stn,0); 
28813                         var selection = editorcore.getSelection();
28814                         selection.removeAllRanges();
28815                         selection.addRange(range);
28816                         
28817                         
28818                         //_this.updateToolbar(null, null, pn);
28819                         _this.updateToolbar(null, null, null);
28820                         _this.updateFooter(false);
28821                         
28822                     }
28823                 }
28824                 
28825                         
28826                     
28827                 
28828             });
28829         }    
28830         
28831         tb.el.on('click', function(e){
28832             e.preventDefault(); // what does this do?
28833         });
28834         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28835         tb.el.hide();
28836         
28837         // dont need to disable them... as they will get hidden
28838         return tb;
28839          
28840         
28841     },
28842     buildFooter : function()
28843     {
28844         
28845         var fel = this.editor.wrap.createChild();
28846         this.footer = new Roo.Toolbar(fel);
28847         // toolbar has scrolly on left / right?
28848         var footDisp= new Roo.Toolbar.Fill();
28849         var _t = this;
28850         this.footer.add(
28851             {
28852                 text : '&lt;',
28853                 xtype: 'Button',
28854                 handler : function() {
28855                     _t.footDisp.scrollTo('left',0,true)
28856                 }
28857             }
28858         );
28859         this.footer.add( footDisp );
28860         this.footer.add( 
28861             {
28862                 text : '&gt;',
28863                 xtype: 'Button',
28864                 handler : function() {
28865                     // no animation..
28866                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28867                 }
28868             }
28869         );
28870         var fel = Roo.get(footDisp.el);
28871         fel.addClass('x-editor-context');
28872         this.footDispWrap = fel; 
28873         this.footDispWrap.overflow  = 'hidden';
28874         
28875         this.footDisp = fel.createChild();
28876         this.footDispWrap.on('click', this.onContextClick, this)
28877         
28878         
28879     },
28880     // when the footer contect changes
28881     onContextClick : function (ev,dom)
28882     {
28883         ev.preventDefault();
28884         var  cn = dom.className;
28885         //Roo.log(cn);
28886         if (!cn.match(/x-ed-loc-/)) {
28887             return;
28888         }
28889         var n = cn.split('-').pop();
28890         var ans = this.footerEls;
28891         var sel = ans[n];
28892         
28893         this.editorcore.selectNode(sel);
28894         
28895         
28896         this.updateToolbar(null, null, sel);
28897         
28898         
28899     }
28900     
28901     
28902     
28903     
28904     
28905 });
28906
28907
28908
28909
28910
28911 /*
28912  * Based on:
28913  * Ext JS Library 1.1.1
28914  * Copyright(c) 2006-2007, Ext JS, LLC.
28915  *
28916  * Originally Released Under LGPL - original licence link has changed is not relivant.
28917  *
28918  * Fork - LGPL
28919  * <script type="text/javascript">
28920  */
28921  
28922 /**
28923  * @class Roo.form.BasicForm
28924  * @extends Roo.util.Observable
28925  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28926  * @constructor
28927  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28928  * @param {Object} config Configuration options
28929  */
28930 Roo.form.BasicForm = function(el, config){
28931     this.allItems = [];
28932     this.childForms = [];
28933     Roo.apply(this, config);
28934     /*
28935      * The Roo.form.Field items in this form.
28936      * @type MixedCollection
28937      */
28938      
28939      
28940     this.items = new Roo.util.MixedCollection(false, function(o){
28941         return o.id || (o.id = Roo.id());
28942     });
28943     this.addEvents({
28944         /**
28945          * @event beforeaction
28946          * Fires before any action is performed. Return false to cancel the action.
28947          * @param {Form} this
28948          * @param {Action} action The action to be performed
28949          */
28950         beforeaction: true,
28951         /**
28952          * @event actionfailed
28953          * Fires when an action fails.
28954          * @param {Form} this
28955          * @param {Action} action The action that failed
28956          */
28957         actionfailed : true,
28958         /**
28959          * @event actioncomplete
28960          * Fires when an action is completed.
28961          * @param {Form} this
28962          * @param {Action} action The action that completed
28963          */
28964         actioncomplete : true
28965     });
28966     if(el){
28967         this.initEl(el);
28968     }
28969     Roo.form.BasicForm.superclass.constructor.call(this);
28970     
28971     Roo.form.BasicForm.popover.apply();
28972 };
28973
28974 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28975     /**
28976      * @cfg {String} method
28977      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28978      */
28979     /**
28980      * @cfg {DataReader} reader
28981      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28982      * This is optional as there is built-in support for processing JSON.
28983      */
28984     /**
28985      * @cfg {DataReader} errorReader
28986      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28987      * This is completely optional as there is built-in support for processing JSON.
28988      */
28989     /**
28990      * @cfg {String} url
28991      * The URL to use for form actions if one isn't supplied in the action options.
28992      */
28993     /**
28994      * @cfg {Boolean} fileUpload
28995      * Set to true if this form is a file upload.
28996      */
28997      
28998     /**
28999      * @cfg {Object} baseParams
29000      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
29001      */
29002      /**
29003      
29004     /**
29005      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
29006      */
29007     timeout: 30,
29008
29009     // private
29010     activeAction : null,
29011
29012     /**
29013      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
29014      * or setValues() data instead of when the form was first created.
29015      */
29016     trackResetOnLoad : false,
29017     
29018     
29019     /**
29020      * childForms - used for multi-tab forms
29021      * @type {Array}
29022      */
29023     childForms : false,
29024     
29025     /**
29026      * allItems - full list of fields.
29027      * @type {Array}
29028      */
29029     allItems : false,
29030     
29031     /**
29032      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
29033      * element by passing it or its id or mask the form itself by passing in true.
29034      * @type Mixed
29035      */
29036     waitMsgTarget : false,
29037     
29038     /**
29039      * @type Boolean
29040      */
29041     disableMask : false,
29042     
29043     /**
29044      * @cfg {Boolean} errorMask (true|false) default false
29045      */
29046     errorMask : false,
29047     
29048     /**
29049      * @cfg {Number} maskOffset Default 100
29050      */
29051     maskOffset : 100,
29052
29053     // private
29054     initEl : function(el){
29055         this.el = Roo.get(el);
29056         this.id = this.el.id || Roo.id();
29057         this.el.on('submit', this.onSubmit, this);
29058         this.el.addClass('x-form');
29059     },
29060
29061     // private
29062     onSubmit : function(e){
29063         e.stopEvent();
29064     },
29065
29066     /**
29067      * Returns true if client-side validation on the form is successful.
29068      * @return Boolean
29069      */
29070     isValid : function(){
29071         var valid = true;
29072         var target = false;
29073         this.items.each(function(f){
29074             if(f.validate()){
29075                 return;
29076             }
29077             
29078             valid = false;
29079                 
29080             if(!target && f.el.isVisible(true)){
29081                 target = f;
29082             }
29083         });
29084         
29085         if(this.errorMask && !valid){
29086             Roo.form.BasicForm.popover.mask(this, target);
29087         }
29088         
29089         return valid;
29090     },
29091     /**
29092      * Returns array of invalid form fields.
29093      * @return Array
29094      */
29095     
29096     invalidFields : function()
29097     {
29098         var ret = [];
29099         this.items.each(function(f){
29100             if(f.validate()){
29101                 return;
29102             }
29103             ret.push(f);
29104             
29105         });
29106         
29107         return ret;
29108     },
29109     
29110     
29111     /**
29112      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29113      * @return Boolean
29114      */
29115     isDirty : function(){
29116         var dirty = false;
29117         this.items.each(function(f){
29118            if(f.isDirty()){
29119                dirty = true;
29120                return false;
29121            }
29122         });
29123         return dirty;
29124     },
29125     
29126     /**
29127      * Returns true if any fields in this form have changed since their original load. (New version)
29128      * @return Boolean
29129      */
29130     
29131     hasChanged : function()
29132     {
29133         var dirty = false;
29134         this.items.each(function(f){
29135            if(f.hasChanged()){
29136                dirty = true;
29137                return false;
29138            }
29139         });
29140         return dirty;
29141         
29142     },
29143     /**
29144      * Resets all hasChanged to 'false' -
29145      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29146      * So hasChanged storage is only to be used for this purpose
29147      * @return Boolean
29148      */
29149     resetHasChanged : function()
29150     {
29151         this.items.each(function(f){
29152            f.resetHasChanged();
29153         });
29154         
29155     },
29156     
29157     
29158     /**
29159      * Performs a predefined action (submit or load) or custom actions you define on this form.
29160      * @param {String} actionName The name of the action type
29161      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29162      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29163      * accept other config options):
29164      * <pre>
29165 Property          Type             Description
29166 ----------------  ---------------  ----------------------------------------------------------------------------------
29167 url               String           The url for the action (defaults to the form's url)
29168 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29169 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29170 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29171                                    validate the form on the client (defaults to false)
29172      * </pre>
29173      * @return {BasicForm} this
29174      */
29175     doAction : function(action, options){
29176         if(typeof action == 'string'){
29177             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29178         }
29179         if(this.fireEvent('beforeaction', this, action) !== false){
29180             this.beforeAction(action);
29181             action.run.defer(100, action);
29182         }
29183         return this;
29184     },
29185
29186     /**
29187      * Shortcut to do a submit action.
29188      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29189      * @return {BasicForm} this
29190      */
29191     submit : function(options){
29192         this.doAction('submit', options);
29193         return this;
29194     },
29195
29196     /**
29197      * Shortcut to do a load action.
29198      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29199      * @return {BasicForm} this
29200      */
29201     load : function(options){
29202         this.doAction('load', options);
29203         return this;
29204     },
29205
29206     /**
29207      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29208      * @param {Record} record The record to edit
29209      * @return {BasicForm} this
29210      */
29211     updateRecord : function(record){
29212         record.beginEdit();
29213         var fs = record.fields;
29214         fs.each(function(f){
29215             var field = this.findField(f.name);
29216             if(field){
29217                 record.set(f.name, field.getValue());
29218             }
29219         }, this);
29220         record.endEdit();
29221         return this;
29222     },
29223
29224     /**
29225      * Loads an Roo.data.Record into this form.
29226      * @param {Record} record The record to load
29227      * @return {BasicForm} this
29228      */
29229     loadRecord : function(record){
29230         this.setValues(record.data);
29231         return this;
29232     },
29233
29234     // private
29235     beforeAction : function(action){
29236         var o = action.options;
29237         
29238         if(!this.disableMask) {
29239             if(this.waitMsgTarget === true){
29240                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29241             }else if(this.waitMsgTarget){
29242                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29243                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29244             }else {
29245                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29246             }
29247         }
29248         
29249          
29250     },
29251
29252     // private
29253     afterAction : function(action, success){
29254         this.activeAction = null;
29255         var o = action.options;
29256         
29257         if(!this.disableMask) {
29258             if(this.waitMsgTarget === true){
29259                 this.el.unmask();
29260             }else if(this.waitMsgTarget){
29261                 this.waitMsgTarget.unmask();
29262             }else{
29263                 Roo.MessageBox.updateProgress(1);
29264                 Roo.MessageBox.hide();
29265             }
29266         }
29267         
29268         if(success){
29269             if(o.reset){
29270                 this.reset();
29271             }
29272             Roo.callback(o.success, o.scope, [this, action]);
29273             this.fireEvent('actioncomplete', this, action);
29274             
29275         }else{
29276             
29277             // failure condition..
29278             // we have a scenario where updates need confirming.
29279             // eg. if a locking scenario exists..
29280             // we look for { errors : { needs_confirm : true }} in the response.
29281             if (
29282                 (typeof(action.result) != 'undefined')  &&
29283                 (typeof(action.result.errors) != 'undefined')  &&
29284                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29285            ){
29286                 var _t = this;
29287                 Roo.MessageBox.confirm(
29288                     "Change requires confirmation",
29289                     action.result.errorMsg,
29290                     function(r) {
29291                         if (r != 'yes') {
29292                             return;
29293                         }
29294                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29295                     }
29296                     
29297                 );
29298                 
29299                 
29300                 
29301                 return;
29302             }
29303             
29304             Roo.callback(o.failure, o.scope, [this, action]);
29305             // show an error message if no failed handler is set..
29306             if (!this.hasListener('actionfailed')) {
29307                 Roo.MessageBox.alert("Error",
29308                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29309                         action.result.errorMsg :
29310                         "Saving Failed, please check your entries or try again"
29311                 );
29312             }
29313             
29314             this.fireEvent('actionfailed', this, action);
29315         }
29316         
29317     },
29318
29319     /**
29320      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29321      * @param {String} id The value to search for
29322      * @return Field
29323      */
29324     findField : function(id){
29325         var field = this.items.get(id);
29326         if(!field){
29327             this.items.each(function(f){
29328                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29329                     field = f;
29330                     return false;
29331                 }
29332             });
29333         }
29334         return field || null;
29335     },
29336
29337     /**
29338      * Add a secondary form to this one, 
29339      * Used to provide tabbed forms. One form is primary, with hidden values 
29340      * which mirror the elements from the other forms.
29341      * 
29342      * @param {Roo.form.Form} form to add.
29343      * 
29344      */
29345     addForm : function(form)
29346     {
29347        
29348         if (this.childForms.indexOf(form) > -1) {
29349             // already added..
29350             return;
29351         }
29352         this.childForms.push(form);
29353         var n = '';
29354         Roo.each(form.allItems, function (fe) {
29355             
29356             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29357             if (this.findField(n)) { // already added..
29358                 return;
29359             }
29360             var add = new Roo.form.Hidden({
29361                 name : n
29362             });
29363             add.render(this.el);
29364             
29365             this.add( add );
29366         }, this);
29367         
29368     },
29369     /**
29370      * Mark fields in this form invalid in bulk.
29371      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29372      * @return {BasicForm} this
29373      */
29374     markInvalid : function(errors){
29375         if(errors instanceof Array){
29376             for(var i = 0, len = errors.length; i < len; i++){
29377                 var fieldError = errors[i];
29378                 var f = this.findField(fieldError.id);
29379                 if(f){
29380                     f.markInvalid(fieldError.msg);
29381                 }
29382             }
29383         }else{
29384             var field, id;
29385             for(id in errors){
29386                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29387                     field.markInvalid(errors[id]);
29388                 }
29389             }
29390         }
29391         Roo.each(this.childForms || [], function (f) {
29392             f.markInvalid(errors);
29393         });
29394         
29395         return this;
29396     },
29397
29398     /**
29399      * Set values for fields in this form in bulk.
29400      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29401      * @return {BasicForm} this
29402      */
29403     setValues : function(values){
29404         if(values instanceof Array){ // array of objects
29405             for(var i = 0, len = values.length; i < len; i++){
29406                 var v = values[i];
29407                 var f = this.findField(v.id);
29408                 if(f){
29409                     f.setValue(v.value);
29410                     if(this.trackResetOnLoad){
29411                         f.originalValue = f.getValue();
29412                     }
29413                 }
29414             }
29415         }else{ // object hash
29416             var field, id;
29417             for(id in values){
29418                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29419                     
29420                     if (field.setFromData && 
29421                         field.valueField && 
29422                         field.displayField &&
29423                         // combos' with local stores can 
29424                         // be queried via setValue()
29425                         // to set their value..
29426                         (field.store && !field.store.isLocal)
29427                         ) {
29428                         // it's a combo
29429                         var sd = { };
29430                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29431                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29432                         field.setFromData(sd);
29433                         
29434                     } else {
29435                         field.setValue(values[id]);
29436                     }
29437                     
29438                     
29439                     if(this.trackResetOnLoad){
29440                         field.originalValue = field.getValue();
29441                     }
29442                 }
29443             }
29444         }
29445         this.resetHasChanged();
29446         
29447         
29448         Roo.each(this.childForms || [], function (f) {
29449             f.setValues(values);
29450             f.resetHasChanged();
29451         });
29452                 
29453         return this;
29454     },
29455  
29456     /**
29457      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29458      * they are returned as an array.
29459      * @param {Boolean} asString
29460      * @return {Object}
29461      */
29462     getValues : function(asString)
29463     {
29464         if (this.childForms) {
29465             // copy values from the child forms
29466             Roo.each(this.childForms, function (f) {
29467                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29468             }, this);
29469         }
29470         
29471         // use formdata
29472         if (typeof(FormData) != 'undefined' && asString !== true) {
29473             // this relies on a 'recent' version of chrome apparently...
29474             try {
29475                 var fd = (new FormData(this.el.dom)).entries();
29476                 var ret = {};
29477                 var ent = fd.next();
29478                 while (!ent.done) {
29479                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29480                     ent = fd.next();
29481                 };
29482                 return ret;
29483             } catch(e) {
29484                 
29485             }
29486             
29487         }
29488         
29489         
29490         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29491         if(asString === true){
29492             return fs;
29493         }
29494         return Roo.urlDecode(fs);
29495     },
29496     
29497     /**
29498      * Returns the fields in this form as an object with key/value pairs. 
29499      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29500      * Normally this will not return readOnly data 
29501      * @param {Boolean} with_readonly return readonly field data.
29502      * @return {Object}
29503      */
29504     getFieldValues : function(with_readonly)
29505     {
29506         if (this.childForms) {
29507             // copy values from the child forms
29508             // should this call getFieldValues - probably not as we do not currently copy
29509             // hidden fields when we generate..
29510             Roo.each(this.childForms, function (f) {
29511                 this.setValues(f.getFieldValues());
29512             }, this);
29513         }
29514         
29515         var ret = {};
29516         this.items.each(function(f){
29517             
29518             if (f.readOnly && with_readonly !== true) {
29519                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29520                         // if a subform contains a copy of them.
29521                         // if you have subforms with the same editable data, you will need to copy the data back
29522                         // and forth.
29523             }
29524             
29525             if (!f.getName()) {
29526                 return;
29527             }
29528             var v = f.getValue();
29529             if (f.inputType =='radio') {
29530                 if (typeof(ret[f.getName()]) == 'undefined') {
29531                     ret[f.getName()] = ''; // empty..
29532                 }
29533                 
29534                 if (!f.el.dom.checked) {
29535                     return;
29536                     
29537                 }
29538                 v = f.el.dom.value;
29539                 
29540             }
29541             
29542             // not sure if this supported any more..
29543             if ((typeof(v) == 'object') && f.getRawValue) {
29544                 v = f.getRawValue() ; // dates..
29545             }
29546             // combo boxes where name != hiddenName...
29547             if (f.name != f.getName()) {
29548                 ret[f.name] = f.getRawValue();
29549             }
29550             ret[f.getName()] = v;
29551         });
29552         
29553         return ret;
29554     },
29555
29556     /**
29557      * Clears all invalid messages in this form.
29558      * @return {BasicForm} this
29559      */
29560     clearInvalid : function(){
29561         this.items.each(function(f){
29562            f.clearInvalid();
29563         });
29564         
29565         Roo.each(this.childForms || [], function (f) {
29566             f.clearInvalid();
29567         });
29568         
29569         
29570         return this;
29571     },
29572
29573     /**
29574      * Resets this form.
29575      * @return {BasicForm} this
29576      */
29577     reset : function(){
29578         this.items.each(function(f){
29579             f.reset();
29580         });
29581         
29582         Roo.each(this.childForms || [], function (f) {
29583             f.reset();
29584         });
29585         this.resetHasChanged();
29586         
29587         return this;
29588     },
29589
29590     /**
29591      * Add Roo.form components to this form.
29592      * @param {Field} field1
29593      * @param {Field} field2 (optional)
29594      * @param {Field} etc (optional)
29595      * @return {BasicForm} this
29596      */
29597     add : function(){
29598         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29599         return this;
29600     },
29601
29602
29603     /**
29604      * Removes a field from the items collection (does NOT remove its markup).
29605      * @param {Field} field
29606      * @return {BasicForm} this
29607      */
29608     remove : function(field){
29609         this.items.remove(field);
29610         return this;
29611     },
29612
29613     /**
29614      * Looks at the fields in this form, checks them for an id attribute,
29615      * and calls applyTo on the existing dom element with that id.
29616      * @return {BasicForm} this
29617      */
29618     render : function(){
29619         this.items.each(function(f){
29620             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29621                 f.applyTo(f.id);
29622             }
29623         });
29624         return this;
29625     },
29626
29627     /**
29628      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29629      * @param {Object} values
29630      * @return {BasicForm} this
29631      */
29632     applyToFields : function(o){
29633         this.items.each(function(f){
29634            Roo.apply(f, o);
29635         });
29636         return this;
29637     },
29638
29639     /**
29640      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29641      * @param {Object} values
29642      * @return {BasicForm} this
29643      */
29644     applyIfToFields : function(o){
29645         this.items.each(function(f){
29646            Roo.applyIf(f, o);
29647         });
29648         return this;
29649     }
29650 });
29651
29652 // back compat
29653 Roo.BasicForm = Roo.form.BasicForm;
29654
29655 Roo.apply(Roo.form.BasicForm, {
29656     
29657     popover : {
29658         
29659         padding : 5,
29660         
29661         isApplied : false,
29662         
29663         isMasked : false,
29664         
29665         form : false,
29666         
29667         target : false,
29668         
29669         intervalID : false,
29670         
29671         maskEl : false,
29672         
29673         apply : function()
29674         {
29675             if(this.isApplied){
29676                 return;
29677             }
29678             
29679             this.maskEl = {
29680                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29681                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29682                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29683                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29684             };
29685             
29686             this.maskEl.top.enableDisplayMode("block");
29687             this.maskEl.left.enableDisplayMode("block");
29688             this.maskEl.bottom.enableDisplayMode("block");
29689             this.maskEl.right.enableDisplayMode("block");
29690             
29691             Roo.get(document.body).on('click', function(){
29692                 this.unmask();
29693             }, this);
29694             
29695             Roo.get(document.body).on('touchstart', function(){
29696                 this.unmask();
29697             }, this);
29698             
29699             this.isApplied = true
29700         },
29701         
29702         mask : function(form, target)
29703         {
29704             this.form = form;
29705             
29706             this.target = target;
29707             
29708             if(!this.form.errorMask || !target.el){
29709                 return;
29710             }
29711             
29712             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29713             
29714             var ot = this.target.el.calcOffsetsTo(scrollable);
29715             
29716             var scrollTo = ot[1] - this.form.maskOffset;
29717             
29718             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29719             
29720             scrollable.scrollTo('top', scrollTo);
29721             
29722             var el = this.target.wrap || this.target.el;
29723             
29724             var box = el.getBox();
29725             
29726             this.maskEl.top.setStyle('position', 'absolute');
29727             this.maskEl.top.setStyle('z-index', 10000);
29728             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29729             this.maskEl.top.setLeft(0);
29730             this.maskEl.top.setTop(0);
29731             this.maskEl.top.show();
29732             
29733             this.maskEl.left.setStyle('position', 'absolute');
29734             this.maskEl.left.setStyle('z-index', 10000);
29735             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29736             this.maskEl.left.setLeft(0);
29737             this.maskEl.left.setTop(box.y - this.padding);
29738             this.maskEl.left.show();
29739
29740             this.maskEl.bottom.setStyle('position', 'absolute');
29741             this.maskEl.bottom.setStyle('z-index', 10000);
29742             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29743             this.maskEl.bottom.setLeft(0);
29744             this.maskEl.bottom.setTop(box.bottom + this.padding);
29745             this.maskEl.bottom.show();
29746
29747             this.maskEl.right.setStyle('position', 'absolute');
29748             this.maskEl.right.setStyle('z-index', 10000);
29749             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29750             this.maskEl.right.setLeft(box.right + this.padding);
29751             this.maskEl.right.setTop(box.y - this.padding);
29752             this.maskEl.right.show();
29753
29754             this.intervalID = window.setInterval(function() {
29755                 Roo.form.BasicForm.popover.unmask();
29756             }, 10000);
29757
29758             window.onwheel = function(){ return false;};
29759             
29760             (function(){ this.isMasked = true; }).defer(500, this);
29761             
29762         },
29763         
29764         unmask : function()
29765         {
29766             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29767                 return;
29768             }
29769             
29770             this.maskEl.top.setStyle('position', 'absolute');
29771             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29772             this.maskEl.top.hide();
29773
29774             this.maskEl.left.setStyle('position', 'absolute');
29775             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29776             this.maskEl.left.hide();
29777
29778             this.maskEl.bottom.setStyle('position', 'absolute');
29779             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29780             this.maskEl.bottom.hide();
29781
29782             this.maskEl.right.setStyle('position', 'absolute');
29783             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29784             this.maskEl.right.hide();
29785             
29786             window.onwheel = function(){ return true;};
29787             
29788             if(this.intervalID){
29789                 window.clearInterval(this.intervalID);
29790                 this.intervalID = false;
29791             }
29792             
29793             this.isMasked = false;
29794             
29795         }
29796         
29797     }
29798     
29799 });/*
29800  * Based on:
29801  * Ext JS Library 1.1.1
29802  * Copyright(c) 2006-2007, Ext JS, LLC.
29803  *
29804  * Originally Released Under LGPL - original licence link has changed is not relivant.
29805  *
29806  * Fork - LGPL
29807  * <script type="text/javascript">
29808  */
29809
29810 /**
29811  * @class Roo.form.Form
29812  * @extends Roo.form.BasicForm
29813  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29814  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29815  * @constructor
29816  * @param {Object} config Configuration options
29817  */
29818 Roo.form.Form = function(config){
29819     var xitems =  [];
29820     if (config.items) {
29821         xitems = config.items;
29822         delete config.items;
29823     }
29824    
29825     
29826     Roo.form.Form.superclass.constructor.call(this, null, config);
29827     this.url = this.url || this.action;
29828     if(!this.root){
29829         this.root = new Roo.form.Layout(Roo.applyIf({
29830             id: Roo.id()
29831         }, config));
29832     }
29833     this.active = this.root;
29834     /**
29835      * Array of all the buttons that have been added to this form via {@link addButton}
29836      * @type Array
29837      */
29838     this.buttons = [];
29839     this.allItems = [];
29840     this.addEvents({
29841         /**
29842          * @event clientvalidation
29843          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29844          * @param {Form} this
29845          * @param {Boolean} valid true if the form has passed client-side validation
29846          */
29847         clientvalidation: true,
29848         /**
29849          * @event rendered
29850          * Fires when the form is rendered
29851          * @param {Roo.form.Form} form
29852          */
29853         rendered : true
29854     });
29855     
29856     if (this.progressUrl) {
29857             // push a hidden field onto the list of fields..
29858             this.addxtype( {
29859                     xns: Roo.form, 
29860                     xtype : 'Hidden', 
29861                     name : 'UPLOAD_IDENTIFIER' 
29862             });
29863         }
29864         
29865     
29866     Roo.each(xitems, this.addxtype, this);
29867     
29868 };
29869
29870 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29871      /**
29872      * @cfg {Roo.Button} buttons[] buttons at bottom of form
29873      */
29874     
29875     /**
29876      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29877      */
29878     /**
29879      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29880      */
29881     /**
29882      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29883      */
29884     buttonAlign:'center',
29885
29886     /**
29887      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29888      */
29889     minButtonWidth:75,
29890
29891     /**
29892      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
29893      * This property cascades to child containers if not set.
29894      */
29895     labelAlign:'left',
29896
29897     /**
29898      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29899      * fires a looping event with that state. This is required to bind buttons to the valid
29900      * state using the config value formBind:true on the button.
29901      */
29902     monitorValid : false,
29903
29904     /**
29905      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29906      */
29907     monitorPoll : 200,
29908     
29909     /**
29910      * @cfg {String} progressUrl - Url to return progress data 
29911      */
29912     
29913     progressUrl : false,
29914     /**
29915      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
29916      * sending a formdata with extra parameters - eg uploaded elements.
29917      */
29918     
29919     formData : false,
29920     
29921     /**
29922      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29923      * fields are added and the column is closed. If no fields are passed the column remains open
29924      * until end() is called.
29925      * @param {Object} config The config to pass to the column
29926      * @param {Field} field1 (optional)
29927      * @param {Field} field2 (optional)
29928      * @param {Field} etc (optional)
29929      * @return Column The column container object
29930      */
29931     column : function(c){
29932         var col = new Roo.form.Column(c);
29933         this.start(col);
29934         if(arguments.length > 1){ // duplicate code required because of Opera
29935             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29936             this.end();
29937         }
29938         return col;
29939     },
29940
29941     /**
29942      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29943      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29944      * until end() is called.
29945      * @param {Object} config The config to pass to the fieldset
29946      * @param {Field} field1 (optional)
29947      * @param {Field} field2 (optional)
29948      * @param {Field} etc (optional)
29949      * @return FieldSet The fieldset container object
29950      */
29951     fieldset : function(c){
29952         var fs = new Roo.form.FieldSet(c);
29953         this.start(fs);
29954         if(arguments.length > 1){ // duplicate code required because of Opera
29955             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29956             this.end();
29957         }
29958         return fs;
29959     },
29960
29961     /**
29962      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29963      * fields are added and the container is closed. If no fields are passed the container remains open
29964      * until end() is called.
29965      * @param {Object} config The config to pass to the Layout
29966      * @param {Field} field1 (optional)
29967      * @param {Field} field2 (optional)
29968      * @param {Field} etc (optional)
29969      * @return Layout The container object
29970      */
29971     container : function(c){
29972         var l = new Roo.form.Layout(c);
29973         this.start(l);
29974         if(arguments.length > 1){ // duplicate code required because of Opera
29975             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29976             this.end();
29977         }
29978         return l;
29979     },
29980
29981     /**
29982      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29983      * @param {Object} container A Roo.form.Layout or subclass of Layout
29984      * @return {Form} this
29985      */
29986     start : function(c){
29987         // cascade label info
29988         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29989         this.active.stack.push(c);
29990         c.ownerCt = this.active;
29991         this.active = c;
29992         return this;
29993     },
29994
29995     /**
29996      * Closes the current open container
29997      * @return {Form} this
29998      */
29999     end : function(){
30000         if(this.active == this.root){
30001             return this;
30002         }
30003         this.active = this.active.ownerCt;
30004         return this;
30005     },
30006
30007     /**
30008      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
30009      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
30010      * as the label of the field.
30011      * @param {Field} field1
30012      * @param {Field} field2 (optional)
30013      * @param {Field} etc. (optional)
30014      * @return {Form} this
30015      */
30016     add : function(){
30017         this.active.stack.push.apply(this.active.stack, arguments);
30018         this.allItems.push.apply(this.allItems,arguments);
30019         var r = [];
30020         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
30021             if(a[i].isFormField){
30022                 r.push(a[i]);
30023             }
30024         }
30025         if(r.length > 0){
30026             Roo.form.Form.superclass.add.apply(this, r);
30027         }
30028         return this;
30029     },
30030     
30031
30032     
30033     
30034     
30035      /**
30036      * Find any element that has been added to a form, using it's ID or name
30037      * This can include framesets, columns etc. along with regular fields..
30038      * @param {String} id - id or name to find.
30039      
30040      * @return {Element} e - or false if nothing found.
30041      */
30042     findbyId : function(id)
30043     {
30044         var ret = false;
30045         if (!id) {
30046             return ret;
30047         }
30048         Roo.each(this.allItems, function(f){
30049             if (f.id == id || f.name == id ){
30050                 ret = f;
30051                 return false;
30052             }
30053         });
30054         return ret;
30055     },
30056
30057     
30058     
30059     /**
30060      * Render this form into the passed container. This should only be called once!
30061      * @param {String/HTMLElement/Element} container The element this component should be rendered into
30062      * @return {Form} this
30063      */
30064     render : function(ct)
30065     {
30066         
30067         
30068         
30069         ct = Roo.get(ct);
30070         var o = this.autoCreate || {
30071             tag: 'form',
30072             method : this.method || 'POST',
30073             id : this.id || Roo.id()
30074         };
30075         this.initEl(ct.createChild(o));
30076
30077         this.root.render(this.el);
30078         
30079        
30080              
30081         this.items.each(function(f){
30082             f.render('x-form-el-'+f.id);
30083         });
30084
30085         if(this.buttons.length > 0){
30086             // tables are required to maintain order and for correct IE layout
30087             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30088                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30089                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30090             }}, null, true);
30091             var tr = tb.getElementsByTagName('tr')[0];
30092             for(var i = 0, len = this.buttons.length; i < len; i++) {
30093                 var b = this.buttons[i];
30094                 var td = document.createElement('td');
30095                 td.className = 'x-form-btn-td';
30096                 b.render(tr.appendChild(td));
30097             }
30098         }
30099         if(this.monitorValid){ // initialize after render
30100             this.startMonitoring();
30101         }
30102         this.fireEvent('rendered', this);
30103         return this;
30104     },
30105
30106     /**
30107      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30108      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30109      * object or a valid Roo.DomHelper element config
30110      * @param {Function} handler The function called when the button is clicked
30111      * @param {Object} scope (optional) The scope of the handler function
30112      * @return {Roo.Button}
30113      */
30114     addButton : function(config, handler, scope){
30115         var bc = {
30116             handler: handler,
30117             scope: scope,
30118             minWidth: this.minButtonWidth,
30119             hideParent:true
30120         };
30121         if(typeof config == "string"){
30122             bc.text = config;
30123         }else{
30124             Roo.apply(bc, config);
30125         }
30126         var btn = new Roo.Button(null, bc);
30127         this.buttons.push(btn);
30128         return btn;
30129     },
30130
30131      /**
30132      * Adds a series of form elements (using the xtype property as the factory method.
30133      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30134      * @param {Object} config 
30135      */
30136     
30137     addxtype : function()
30138     {
30139         var ar = Array.prototype.slice.call(arguments, 0);
30140         var ret = false;
30141         for(var i = 0; i < ar.length; i++) {
30142             if (!ar[i]) {
30143                 continue; // skip -- if this happends something invalid got sent, we 
30144                 // should ignore it, as basically that interface element will not show up
30145                 // and that should be pretty obvious!!
30146             }
30147             
30148             if (Roo.form[ar[i].xtype]) {
30149                 ar[i].form = this;
30150                 var fe = Roo.factory(ar[i], Roo.form);
30151                 if (!ret) {
30152                     ret = fe;
30153                 }
30154                 fe.form = this;
30155                 if (fe.store) {
30156                     fe.store.form = this;
30157                 }
30158                 if (fe.isLayout) {  
30159                          
30160                     this.start(fe);
30161                     this.allItems.push(fe);
30162                     if (fe.items && fe.addxtype) {
30163                         fe.addxtype.apply(fe, fe.items);
30164                         delete fe.items;
30165                     }
30166                      this.end();
30167                     continue;
30168                 }
30169                 
30170                 
30171                  
30172                 this.add(fe);
30173               //  console.log('adding ' + ar[i].xtype);
30174             }
30175             if (ar[i].xtype == 'Button') {  
30176                 //console.log('adding button');
30177                 //console.log(ar[i]);
30178                 this.addButton(ar[i]);
30179                 this.allItems.push(fe);
30180                 continue;
30181             }
30182             
30183             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30184                 alert('end is not supported on xtype any more, use items');
30185             //    this.end();
30186             //    //console.log('adding end');
30187             }
30188             
30189         }
30190         return ret;
30191     },
30192     
30193     /**
30194      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30195      * option "monitorValid"
30196      */
30197     startMonitoring : function(){
30198         if(!this.bound){
30199             this.bound = true;
30200             Roo.TaskMgr.start({
30201                 run : this.bindHandler,
30202                 interval : this.monitorPoll || 200,
30203                 scope: this
30204             });
30205         }
30206     },
30207
30208     /**
30209      * Stops monitoring of the valid state of this form
30210      */
30211     stopMonitoring : function(){
30212         this.bound = false;
30213     },
30214
30215     // private
30216     bindHandler : function(){
30217         if(!this.bound){
30218             return false; // stops binding
30219         }
30220         var valid = true;
30221         this.items.each(function(f){
30222             if(!f.isValid(true)){
30223                 valid = false;
30224                 return false;
30225             }
30226         });
30227         for(var i = 0, len = this.buttons.length; i < len; i++){
30228             var btn = this.buttons[i];
30229             if(btn.formBind === true && btn.disabled === valid){
30230                 btn.setDisabled(!valid);
30231             }
30232         }
30233         this.fireEvent('clientvalidation', this, valid);
30234     }
30235     
30236     
30237     
30238     
30239     
30240     
30241     
30242     
30243 });
30244
30245
30246 // back compat
30247 Roo.Form = Roo.form.Form;
30248 /*
30249  * Based on:
30250  * Ext JS Library 1.1.1
30251  * Copyright(c) 2006-2007, Ext JS, LLC.
30252  *
30253  * Originally Released Under LGPL - original licence link has changed is not relivant.
30254  *
30255  * Fork - LGPL
30256  * <script type="text/javascript">
30257  */
30258
30259 // as we use this in bootstrap.
30260 Roo.namespace('Roo.form');
30261  /**
30262  * @class Roo.form.Action
30263  * Internal Class used to handle form actions
30264  * @constructor
30265  * @param {Roo.form.BasicForm} el The form element or its id
30266  * @param {Object} config Configuration options
30267  */
30268
30269  
30270  
30271 // define the action interface
30272 Roo.form.Action = function(form, options){
30273     this.form = form;
30274     this.options = options || {};
30275 };
30276 /**
30277  * Client Validation Failed
30278  * @const 
30279  */
30280 Roo.form.Action.CLIENT_INVALID = 'client';
30281 /**
30282  * Server Validation Failed
30283  * @const 
30284  */
30285 Roo.form.Action.SERVER_INVALID = 'server';
30286  /**
30287  * Connect to Server Failed
30288  * @const 
30289  */
30290 Roo.form.Action.CONNECT_FAILURE = 'connect';
30291 /**
30292  * Reading Data from Server Failed
30293  * @const 
30294  */
30295 Roo.form.Action.LOAD_FAILURE = 'load';
30296
30297 Roo.form.Action.prototype = {
30298     type : 'default',
30299     failureType : undefined,
30300     response : undefined,
30301     result : undefined,
30302
30303     // interface method
30304     run : function(options){
30305
30306     },
30307
30308     // interface method
30309     success : function(response){
30310
30311     },
30312
30313     // interface method
30314     handleResponse : function(response){
30315
30316     },
30317
30318     // default connection failure
30319     failure : function(response){
30320         
30321         this.response = response;
30322         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30323         this.form.afterAction(this, false);
30324     },
30325
30326     processResponse : function(response){
30327         this.response = response;
30328         if(!response.responseText){
30329             return true;
30330         }
30331         this.result = this.handleResponse(response);
30332         return this.result;
30333     },
30334
30335     // utility functions used internally
30336     getUrl : function(appendParams){
30337         var url = this.options.url || this.form.url || this.form.el.dom.action;
30338         if(appendParams){
30339             var p = this.getParams();
30340             if(p){
30341                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30342             }
30343         }
30344         return url;
30345     },
30346
30347     getMethod : function(){
30348         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30349     },
30350
30351     getParams : function(){
30352         var bp = this.form.baseParams;
30353         var p = this.options.params;
30354         if(p){
30355             if(typeof p == "object"){
30356                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30357             }else if(typeof p == 'string' && bp){
30358                 p += '&' + Roo.urlEncode(bp);
30359             }
30360         }else if(bp){
30361             p = Roo.urlEncode(bp);
30362         }
30363         return p;
30364     },
30365
30366     createCallback : function(){
30367         return {
30368             success: this.success,
30369             failure: this.failure,
30370             scope: this,
30371             timeout: (this.form.timeout*1000),
30372             upload: this.form.fileUpload ? this.success : undefined
30373         };
30374     }
30375 };
30376
30377 Roo.form.Action.Submit = function(form, options){
30378     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30379 };
30380
30381 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30382     type : 'submit',
30383
30384     haveProgress : false,
30385     uploadComplete : false,
30386     
30387     // uploadProgress indicator.
30388     uploadProgress : function()
30389     {
30390         if (!this.form.progressUrl) {
30391             return;
30392         }
30393         
30394         if (!this.haveProgress) {
30395             Roo.MessageBox.progress("Uploading", "Uploading");
30396         }
30397         if (this.uploadComplete) {
30398            Roo.MessageBox.hide();
30399            return;
30400         }
30401         
30402         this.haveProgress = true;
30403    
30404         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30405         
30406         var c = new Roo.data.Connection();
30407         c.request({
30408             url : this.form.progressUrl,
30409             params: {
30410                 id : uid
30411             },
30412             method: 'GET',
30413             success : function(req){
30414                //console.log(data);
30415                 var rdata = false;
30416                 var edata;
30417                 try  {
30418                    rdata = Roo.decode(req.responseText)
30419                 } catch (e) {
30420                     Roo.log("Invalid data from server..");
30421                     Roo.log(edata);
30422                     return;
30423                 }
30424                 if (!rdata || !rdata.success) {
30425                     Roo.log(rdata);
30426                     Roo.MessageBox.alert(Roo.encode(rdata));
30427                     return;
30428                 }
30429                 var data = rdata.data;
30430                 
30431                 if (this.uploadComplete) {
30432                    Roo.MessageBox.hide();
30433                    return;
30434                 }
30435                    
30436                 if (data){
30437                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30438                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30439                     );
30440                 }
30441                 this.uploadProgress.defer(2000,this);
30442             },
30443        
30444             failure: function(data) {
30445                 Roo.log('progress url failed ');
30446                 Roo.log(data);
30447             },
30448             scope : this
30449         });
30450            
30451     },
30452     
30453     
30454     run : function()
30455     {
30456         // run get Values on the form, so it syncs any secondary forms.
30457         this.form.getValues();
30458         
30459         var o = this.options;
30460         var method = this.getMethod();
30461         var isPost = method == 'POST';
30462         if(o.clientValidation === false || this.form.isValid()){
30463             
30464             if (this.form.progressUrl) {
30465                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30466                     (new Date() * 1) + '' + Math.random());
30467                     
30468             } 
30469             
30470             
30471             Roo.Ajax.request(Roo.apply(this.createCallback(), {
30472                 form:this.form.el.dom,
30473                 url:this.getUrl(!isPost),
30474                 method: method,
30475                 params:isPost ? this.getParams() : null,
30476                 isUpload: this.form.fileUpload,
30477                 formData : this.form.formData
30478             }));
30479             
30480             this.uploadProgress();
30481
30482         }else if (o.clientValidation !== false){ // client validation failed
30483             this.failureType = Roo.form.Action.CLIENT_INVALID;
30484             this.form.afterAction(this, false);
30485         }
30486     },
30487
30488     success : function(response)
30489     {
30490         this.uploadComplete= true;
30491         if (this.haveProgress) {
30492             Roo.MessageBox.hide();
30493         }
30494         
30495         
30496         var result = this.processResponse(response);
30497         if(result === true || result.success){
30498             this.form.afterAction(this, true);
30499             return;
30500         }
30501         if(result.errors){
30502             this.form.markInvalid(result.errors);
30503             this.failureType = Roo.form.Action.SERVER_INVALID;
30504         }
30505         this.form.afterAction(this, false);
30506     },
30507     failure : function(response)
30508     {
30509         this.uploadComplete= true;
30510         if (this.haveProgress) {
30511             Roo.MessageBox.hide();
30512         }
30513         
30514         this.response = response;
30515         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30516         this.form.afterAction(this, false);
30517     },
30518     
30519     handleResponse : function(response){
30520         if(this.form.errorReader){
30521             var rs = this.form.errorReader.read(response);
30522             var errors = [];
30523             if(rs.records){
30524                 for(var i = 0, len = rs.records.length; i < len; i++) {
30525                     var r = rs.records[i];
30526                     errors[i] = r.data;
30527                 }
30528             }
30529             if(errors.length < 1){
30530                 errors = null;
30531             }
30532             return {
30533                 success : rs.success,
30534                 errors : errors
30535             };
30536         }
30537         var ret = false;
30538         try {
30539             ret = Roo.decode(response.responseText);
30540         } catch (e) {
30541             ret = {
30542                 success: false,
30543                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30544                 errors : []
30545             };
30546         }
30547         return ret;
30548         
30549     }
30550 });
30551
30552
30553 Roo.form.Action.Load = function(form, options){
30554     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30555     this.reader = this.form.reader;
30556 };
30557
30558 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30559     type : 'load',
30560
30561     run : function(){
30562         
30563         Roo.Ajax.request(Roo.apply(
30564                 this.createCallback(), {
30565                     method:this.getMethod(),
30566                     url:this.getUrl(false),
30567                     params:this.getParams()
30568         }));
30569     },
30570
30571     success : function(response){
30572         
30573         var result = this.processResponse(response);
30574         if(result === true || !result.success || !result.data){
30575             this.failureType = Roo.form.Action.LOAD_FAILURE;
30576             this.form.afterAction(this, false);
30577             return;
30578         }
30579         this.form.clearInvalid();
30580         this.form.setValues(result.data);
30581         this.form.afterAction(this, true);
30582     },
30583
30584     handleResponse : function(response){
30585         if(this.form.reader){
30586             var rs = this.form.reader.read(response);
30587             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30588             return {
30589                 success : rs.success,
30590                 data : data
30591             };
30592         }
30593         return Roo.decode(response.responseText);
30594     }
30595 });
30596
30597 Roo.form.Action.ACTION_TYPES = {
30598     'load' : Roo.form.Action.Load,
30599     'submit' : Roo.form.Action.Submit
30600 };/*
30601  * Based on:
30602  * Ext JS Library 1.1.1
30603  * Copyright(c) 2006-2007, Ext JS, LLC.
30604  *
30605  * Originally Released Under LGPL - original licence link has changed is not relivant.
30606  *
30607  * Fork - LGPL
30608  * <script type="text/javascript">
30609  */
30610  
30611 /**
30612  * @class Roo.form.Layout
30613  * @extends Roo.Component
30614  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30615  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30616  * @constructor
30617  * @param {Object} config Configuration options
30618  */
30619 Roo.form.Layout = function(config){
30620     var xitems = [];
30621     if (config.items) {
30622         xitems = config.items;
30623         delete config.items;
30624     }
30625     Roo.form.Layout.superclass.constructor.call(this, config);
30626     this.stack = [];
30627     Roo.each(xitems, this.addxtype, this);
30628      
30629 };
30630
30631 Roo.extend(Roo.form.Layout, Roo.Component, {
30632     /**
30633      * @cfg {String/Object} autoCreate
30634      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30635      */
30636     /**
30637      * @cfg {String/Object/Function} style
30638      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30639      * a function which returns such a specification.
30640      */
30641     /**
30642      * @cfg {String} labelAlign
30643      * Valid values are "left," "top" and "right" (defaults to "left")
30644      */
30645     /**
30646      * @cfg {Number} labelWidth
30647      * Fixed width in pixels of all field labels (defaults to undefined)
30648      */
30649     /**
30650      * @cfg {Boolean} clear
30651      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30652      */
30653     clear : true,
30654     /**
30655      * @cfg {String} labelSeparator
30656      * The separator to use after field labels (defaults to ':')
30657      */
30658     labelSeparator : ':',
30659     /**
30660      * @cfg {Boolean} hideLabels
30661      * True to suppress the display of field labels in this layout (defaults to false)
30662      */
30663     hideLabels : false,
30664
30665     // private
30666     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30667     
30668     isLayout : true,
30669     
30670     // private
30671     onRender : function(ct, position){
30672         if(this.el){ // from markup
30673             this.el = Roo.get(this.el);
30674         }else {  // generate
30675             var cfg = this.getAutoCreate();
30676             this.el = ct.createChild(cfg, position);
30677         }
30678         if(this.style){
30679             this.el.applyStyles(this.style);
30680         }
30681         if(this.labelAlign){
30682             this.el.addClass('x-form-label-'+this.labelAlign);
30683         }
30684         if(this.hideLabels){
30685             this.labelStyle = "display:none";
30686             this.elementStyle = "padding-left:0;";
30687         }else{
30688             if(typeof this.labelWidth == 'number'){
30689                 this.labelStyle = "width:"+this.labelWidth+"px;";
30690                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30691             }
30692             if(this.labelAlign == 'top'){
30693                 this.labelStyle = "width:auto;";
30694                 this.elementStyle = "padding-left:0;";
30695             }
30696         }
30697         var stack = this.stack;
30698         var slen = stack.length;
30699         if(slen > 0){
30700             if(!this.fieldTpl){
30701                 var t = new Roo.Template(
30702                     '<div class="x-form-item {5}">',
30703                         '<label for="{0}" style="{2}">{1}{4}</label>',
30704                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30705                         '</div>',
30706                     '</div><div class="x-form-clear-left"></div>'
30707                 );
30708                 t.disableFormats = true;
30709                 t.compile();
30710                 Roo.form.Layout.prototype.fieldTpl = t;
30711             }
30712             for(var i = 0; i < slen; i++) {
30713                 if(stack[i].isFormField){
30714                     this.renderField(stack[i]);
30715                 }else{
30716                     this.renderComponent(stack[i]);
30717                 }
30718             }
30719         }
30720         if(this.clear){
30721             this.el.createChild({cls:'x-form-clear'});
30722         }
30723     },
30724
30725     // private
30726     renderField : function(f){
30727         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30728                f.id, //0
30729                f.fieldLabel, //1
30730                f.labelStyle||this.labelStyle||'', //2
30731                this.elementStyle||'', //3
30732                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30733                f.itemCls||this.itemCls||''  //5
30734        ], true).getPrevSibling());
30735     },
30736
30737     // private
30738     renderComponent : function(c){
30739         c.render(c.isLayout ? this.el : this.el.createChild());    
30740     },
30741     /**
30742      * Adds a object form elements (using the xtype property as the factory method.)
30743      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30744      * @param {Object} config 
30745      */
30746     addxtype : function(o)
30747     {
30748         // create the lement.
30749         o.form = this.form;
30750         var fe = Roo.factory(o, Roo.form);
30751         this.form.allItems.push(fe);
30752         this.stack.push(fe);
30753         
30754         if (fe.isFormField) {
30755             this.form.items.add(fe);
30756         }
30757          
30758         return fe;
30759     }
30760 });
30761
30762 /**
30763  * @class Roo.form.Column
30764  * @extends Roo.form.Layout
30765  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30766  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30767  * @constructor
30768  * @param {Object} config Configuration options
30769  */
30770 Roo.form.Column = function(config){
30771     Roo.form.Column.superclass.constructor.call(this, config);
30772 };
30773
30774 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30775     /**
30776      * @cfg {Number/String} width
30777      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30778      */
30779     /**
30780      * @cfg {String/Object} autoCreate
30781      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30782      */
30783
30784     // private
30785     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30786
30787     // private
30788     onRender : function(ct, position){
30789         Roo.form.Column.superclass.onRender.call(this, ct, position);
30790         if(this.width){
30791             this.el.setWidth(this.width);
30792         }
30793     }
30794 });
30795
30796
30797 /**
30798  * @class Roo.form.Row
30799  * @extends Roo.form.Layout
30800  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30801  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30802  * @constructor
30803  * @param {Object} config Configuration options
30804  */
30805
30806  
30807 Roo.form.Row = function(config){
30808     Roo.form.Row.superclass.constructor.call(this, config);
30809 };
30810  
30811 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30812       /**
30813      * @cfg {Number/String} width
30814      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30815      */
30816     /**
30817      * @cfg {Number/String} height
30818      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30819      */
30820     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30821     
30822     padWidth : 20,
30823     // private
30824     onRender : function(ct, position){
30825         //console.log('row render');
30826         if(!this.rowTpl){
30827             var t = new Roo.Template(
30828                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30829                     '<label for="{0}" style="{2}">{1}{4}</label>',
30830                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30831                     '</div>',
30832                 '</div>'
30833             );
30834             t.disableFormats = true;
30835             t.compile();
30836             Roo.form.Layout.prototype.rowTpl = t;
30837         }
30838         this.fieldTpl = this.rowTpl;
30839         
30840         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30841         var labelWidth = 100;
30842         
30843         if ((this.labelAlign != 'top')) {
30844             if (typeof this.labelWidth == 'number') {
30845                 labelWidth = this.labelWidth
30846             }
30847             this.padWidth =  20 + labelWidth;
30848             
30849         }
30850         
30851         Roo.form.Column.superclass.onRender.call(this, ct, position);
30852         if(this.width){
30853             this.el.setWidth(this.width);
30854         }
30855         if(this.height){
30856             this.el.setHeight(this.height);
30857         }
30858     },
30859     
30860     // private
30861     renderField : function(f){
30862         f.fieldEl = this.fieldTpl.append(this.el, [
30863                f.id, f.fieldLabel,
30864                f.labelStyle||this.labelStyle||'',
30865                this.elementStyle||'',
30866                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30867                f.itemCls||this.itemCls||'',
30868                f.width ? f.width + this.padWidth : 160 + this.padWidth
30869        ],true);
30870     }
30871 });
30872  
30873
30874 /**
30875  * @class Roo.form.FieldSet
30876  * @extends Roo.form.Layout
30877  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
30878  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30879  * @constructor
30880  * @param {Object} config Configuration options
30881  */
30882 Roo.form.FieldSet = function(config){
30883     Roo.form.FieldSet.superclass.constructor.call(this, config);
30884 };
30885
30886 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30887     /**
30888      * @cfg {String} legend
30889      * The text to display as the legend for the FieldSet (defaults to '')
30890      */
30891     /**
30892      * @cfg {String/Object} autoCreate
30893      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30894      */
30895
30896     // private
30897     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30898
30899     // private
30900     onRender : function(ct, position){
30901         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30902         if(this.legend){
30903             this.setLegend(this.legend);
30904         }
30905     },
30906
30907     // private
30908     setLegend : function(text){
30909         if(this.rendered){
30910             this.el.child('legend').update(text);
30911         }
30912     }
30913 });/*
30914  * Based on:
30915  * Ext JS Library 1.1.1
30916  * Copyright(c) 2006-2007, Ext JS, LLC.
30917  *
30918  * Originally Released Under LGPL - original licence link has changed is not relivant.
30919  *
30920  * Fork - LGPL
30921  * <script type="text/javascript">
30922  */
30923 /**
30924  * @class Roo.form.VTypes
30925  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30926  * @static
30927  */
30928 Roo.form.VTypes = function(){
30929     // closure these in so they are only created once.
30930     var alpha = /^[a-zA-Z_]+$/;
30931     var alphanum = /^[a-zA-Z0-9_]+$/;
30932     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30933     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30934
30935     // All these messages and functions are configurable
30936     return {
30937         /**
30938          * The function used to validate email addresses
30939          * @param {String} value The email address
30940          */
30941         'email' : function(v){
30942             return email.test(v);
30943         },
30944         /**
30945          * The error text to display when the email validation function returns false
30946          * @type String
30947          */
30948         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30949         /**
30950          * The keystroke filter mask to be applied on email input
30951          * @type RegExp
30952          */
30953         'emailMask' : /[a-z0-9_\.\-@]/i,
30954
30955         /**
30956          * The function used to validate URLs
30957          * @param {String} value The URL
30958          */
30959         'url' : function(v){
30960             return url.test(v);
30961         },
30962         /**
30963          * The error text to display when the url validation function returns false
30964          * @type String
30965          */
30966         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30967         
30968         /**
30969          * The function used to validate alpha values
30970          * @param {String} value The value
30971          */
30972         'alpha' : function(v){
30973             return alpha.test(v);
30974         },
30975         /**
30976          * The error text to display when the alpha validation function returns false
30977          * @type String
30978          */
30979         'alphaText' : 'This field should only contain letters and _',
30980         /**
30981          * The keystroke filter mask to be applied on alpha input
30982          * @type RegExp
30983          */
30984         'alphaMask' : /[a-z_]/i,
30985
30986         /**
30987          * The function used to validate alphanumeric values
30988          * @param {String} value The value
30989          */
30990         'alphanum' : function(v){
30991             return alphanum.test(v);
30992         },
30993         /**
30994          * The error text to display when the alphanumeric validation function returns false
30995          * @type String
30996          */
30997         'alphanumText' : 'This field should only contain letters, numbers and _',
30998         /**
30999          * The keystroke filter mask to be applied on alphanumeric input
31000          * @type RegExp
31001          */
31002         'alphanumMask' : /[a-z0-9_]/i
31003     };
31004 }();//<script type="text/javascript">
31005
31006 /**
31007  * @class Roo.form.FCKeditor
31008  * @extends Roo.form.TextArea
31009  * Wrapper around the FCKEditor http://www.fckeditor.net
31010  * @constructor
31011  * Creates a new FCKeditor
31012  * @param {Object} config Configuration options
31013  */
31014 Roo.form.FCKeditor = function(config){
31015     Roo.form.FCKeditor.superclass.constructor.call(this, config);
31016     this.addEvents({
31017          /**
31018          * @event editorinit
31019          * Fired when the editor is initialized - you can add extra handlers here..
31020          * @param {FCKeditor} this
31021          * @param {Object} the FCK object.
31022          */
31023         editorinit : true
31024     });
31025     
31026     
31027 };
31028 Roo.form.FCKeditor.editors = { };
31029 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
31030 {
31031     //defaultAutoCreate : {
31032     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
31033     //},
31034     // private
31035     /**
31036      * @cfg {Object} fck options - see fck manual for details.
31037      */
31038     fckconfig : false,
31039     
31040     /**
31041      * @cfg {Object} fck toolbar set (Basic or Default)
31042      */
31043     toolbarSet : 'Basic',
31044     /**
31045      * @cfg {Object} fck BasePath
31046      */ 
31047     basePath : '/fckeditor/',
31048     
31049     
31050     frame : false,
31051     
31052     value : '',
31053     
31054    
31055     onRender : function(ct, position)
31056     {
31057         if(!this.el){
31058             this.defaultAutoCreate = {
31059                 tag: "textarea",
31060                 style:"width:300px;height:60px;",
31061                 autocomplete: "new-password"
31062             };
31063         }
31064         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31065         /*
31066         if(this.grow){
31067             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31068             if(this.preventScrollbars){
31069                 this.el.setStyle("overflow", "hidden");
31070             }
31071             this.el.setHeight(this.growMin);
31072         }
31073         */
31074         //console.log('onrender' + this.getId() );
31075         Roo.form.FCKeditor.editors[this.getId()] = this;
31076          
31077
31078         this.replaceTextarea() ;
31079         
31080     },
31081     
31082     getEditor : function() {
31083         return this.fckEditor;
31084     },
31085     /**
31086      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31087      * @param {Mixed} value The value to set
31088      */
31089     
31090     
31091     setValue : function(value)
31092     {
31093         //console.log('setValue: ' + value);
31094         
31095         if(typeof(value) == 'undefined') { // not sure why this is happending...
31096             return;
31097         }
31098         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31099         
31100         //if(!this.el || !this.getEditor()) {
31101         //    this.value = value;
31102             //this.setValue.defer(100,this,[value]);    
31103         //    return;
31104         //} 
31105         
31106         if(!this.getEditor()) {
31107             return;
31108         }
31109         
31110         this.getEditor().SetData(value);
31111         
31112         //
31113
31114     },
31115
31116     /**
31117      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31118      * @return {Mixed} value The field value
31119      */
31120     getValue : function()
31121     {
31122         
31123         if (this.frame && this.frame.dom.style.display == 'none') {
31124             return Roo.form.FCKeditor.superclass.getValue.call(this);
31125         }
31126         
31127         if(!this.el || !this.getEditor()) {
31128            
31129            // this.getValue.defer(100,this); 
31130             return this.value;
31131         }
31132        
31133         
31134         var value=this.getEditor().GetData();
31135         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31136         return Roo.form.FCKeditor.superclass.getValue.call(this);
31137         
31138
31139     },
31140
31141     /**
31142      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31143      * @return {Mixed} value The field value
31144      */
31145     getRawValue : function()
31146     {
31147         if (this.frame && this.frame.dom.style.display == 'none') {
31148             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31149         }
31150         
31151         if(!this.el || !this.getEditor()) {
31152             //this.getRawValue.defer(100,this); 
31153             return this.value;
31154             return;
31155         }
31156         
31157         
31158         
31159         var value=this.getEditor().GetData();
31160         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31161         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31162          
31163     },
31164     
31165     setSize : function(w,h) {
31166         
31167         
31168         
31169         //if (this.frame && this.frame.dom.style.display == 'none') {
31170         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31171         //    return;
31172         //}
31173         //if(!this.el || !this.getEditor()) {
31174         //    this.setSize.defer(100,this, [w,h]); 
31175         //    return;
31176         //}
31177         
31178         
31179         
31180         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31181         
31182         this.frame.dom.setAttribute('width', w);
31183         this.frame.dom.setAttribute('height', h);
31184         this.frame.setSize(w,h);
31185         
31186     },
31187     
31188     toggleSourceEdit : function(value) {
31189         
31190       
31191          
31192         this.el.dom.style.display = value ? '' : 'none';
31193         this.frame.dom.style.display = value ?  'none' : '';
31194         
31195     },
31196     
31197     
31198     focus: function(tag)
31199     {
31200         if (this.frame.dom.style.display == 'none') {
31201             return Roo.form.FCKeditor.superclass.focus.call(this);
31202         }
31203         if(!this.el || !this.getEditor()) {
31204             this.focus.defer(100,this, [tag]); 
31205             return;
31206         }
31207         
31208         
31209         
31210         
31211         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31212         this.getEditor().Focus();
31213         if (tgs.length) {
31214             if (!this.getEditor().Selection.GetSelection()) {
31215                 this.focus.defer(100,this, [tag]); 
31216                 return;
31217             }
31218             
31219             
31220             var r = this.getEditor().EditorDocument.createRange();
31221             r.setStart(tgs[0],0);
31222             r.setEnd(tgs[0],0);
31223             this.getEditor().Selection.GetSelection().removeAllRanges();
31224             this.getEditor().Selection.GetSelection().addRange(r);
31225             this.getEditor().Focus();
31226         }
31227         
31228     },
31229     
31230     
31231     
31232     replaceTextarea : function()
31233     {
31234         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31235             return ;
31236         }
31237         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31238         //{
31239             // We must check the elements firstly using the Id and then the name.
31240         var oTextarea = document.getElementById( this.getId() );
31241         
31242         var colElementsByName = document.getElementsByName( this.getId() ) ;
31243          
31244         oTextarea.style.display = 'none' ;
31245
31246         if ( oTextarea.tabIndex ) {            
31247             this.TabIndex = oTextarea.tabIndex ;
31248         }
31249         
31250         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31251         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31252         this.frame = Roo.get(this.getId() + '___Frame')
31253     },
31254     
31255     _getConfigHtml : function()
31256     {
31257         var sConfig = '' ;
31258
31259         for ( var o in this.fckconfig ) {
31260             sConfig += sConfig.length > 0  ? '&amp;' : '';
31261             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31262         }
31263
31264         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31265     },
31266     
31267     
31268     _getIFrameHtml : function()
31269     {
31270         var sFile = 'fckeditor.html' ;
31271         /* no idea what this is about..
31272         try
31273         {
31274             if ( (/fcksource=true/i).test( window.top.location.search ) )
31275                 sFile = 'fckeditor.original.html' ;
31276         }
31277         catch (e) { 
31278         */
31279
31280         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31281         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31282         
31283         
31284         var html = '<iframe id="' + this.getId() +
31285             '___Frame" src="' + sLink +
31286             '" width="' + this.width +
31287             '" height="' + this.height + '"' +
31288             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31289             ' frameborder="0" scrolling="no"></iframe>' ;
31290
31291         return html ;
31292     },
31293     
31294     _insertHtmlBefore : function( html, element )
31295     {
31296         if ( element.insertAdjacentHTML )       {
31297             // IE
31298             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31299         } else { // Gecko
31300             var oRange = document.createRange() ;
31301             oRange.setStartBefore( element ) ;
31302             var oFragment = oRange.createContextualFragment( html );
31303             element.parentNode.insertBefore( oFragment, element ) ;
31304         }
31305     }
31306     
31307     
31308   
31309     
31310     
31311     
31312     
31313
31314 });
31315
31316 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31317
31318 function FCKeditor_OnComplete(editorInstance){
31319     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31320     f.fckEditor = editorInstance;
31321     //console.log("loaded");
31322     f.fireEvent('editorinit', f, editorInstance);
31323
31324   
31325
31326  
31327
31328
31329
31330
31331
31332
31333
31334
31335
31336
31337
31338
31339
31340
31341
31342 //<script type="text/javascript">
31343 /**
31344  * @class Roo.form.GridField
31345  * @extends Roo.form.Field
31346  * Embed a grid (or editable grid into a form)
31347  * STATUS ALPHA
31348  * 
31349  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31350  * it needs 
31351  * xgrid.store = Roo.data.Store
31352  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31353  * xgrid.store.reader = Roo.data.JsonReader 
31354  * 
31355  * 
31356  * @constructor
31357  * Creates a new GridField
31358  * @param {Object} config Configuration options
31359  */
31360 Roo.form.GridField = function(config){
31361     Roo.form.GridField.superclass.constructor.call(this, config);
31362      
31363 };
31364
31365 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31366     /**
31367      * @cfg {Number} width  - used to restrict width of grid..
31368      */
31369     width : 100,
31370     /**
31371      * @cfg {Number} height - used to restrict height of grid..
31372      */
31373     height : 50,
31374      /**
31375      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31376          * 
31377          *}
31378      */
31379     xgrid : false, 
31380     /**
31381      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31382      * {tag: "input", type: "checkbox", autocomplete: "off"})
31383      */
31384    // defaultAutoCreate : { tag: 'div' },
31385     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31386     /**
31387      * @cfg {String} addTitle Text to include for adding a title.
31388      */
31389     addTitle : false,
31390     //
31391     onResize : function(){
31392         Roo.form.Field.superclass.onResize.apply(this, arguments);
31393     },
31394
31395     initEvents : function(){
31396         // Roo.form.Checkbox.superclass.initEvents.call(this);
31397         // has no events...
31398        
31399     },
31400
31401
31402     getResizeEl : function(){
31403         return this.wrap;
31404     },
31405
31406     getPositionEl : function(){
31407         return this.wrap;
31408     },
31409
31410     // private
31411     onRender : function(ct, position){
31412         
31413         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31414         var style = this.style;
31415         delete this.style;
31416         
31417         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31418         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31419         this.viewEl = this.wrap.createChild({ tag: 'div' });
31420         if (style) {
31421             this.viewEl.applyStyles(style);
31422         }
31423         if (this.width) {
31424             this.viewEl.setWidth(this.width);
31425         }
31426         if (this.height) {
31427             this.viewEl.setHeight(this.height);
31428         }
31429         //if(this.inputValue !== undefined){
31430         //this.setValue(this.value);
31431         
31432         
31433         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31434         
31435         
31436         this.grid.render();
31437         this.grid.getDataSource().on('remove', this.refreshValue, this);
31438         this.grid.getDataSource().on('update', this.refreshValue, this);
31439         this.grid.on('afteredit', this.refreshValue, this);
31440  
31441     },
31442      
31443     
31444     /**
31445      * Sets the value of the item. 
31446      * @param {String} either an object  or a string..
31447      */
31448     setValue : function(v){
31449         //this.value = v;
31450         v = v || []; // empty set..
31451         // this does not seem smart - it really only affects memoryproxy grids..
31452         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31453             var ds = this.grid.getDataSource();
31454             // assumes a json reader..
31455             var data = {}
31456             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
31457             ds.loadData( data);
31458         }
31459         // clear selection so it does not get stale.
31460         if (this.grid.sm) { 
31461             this.grid.sm.clearSelections();
31462         }
31463         
31464         Roo.form.GridField.superclass.setValue.call(this, v);
31465         this.refreshValue();
31466         // should load data in the grid really....
31467     },
31468     
31469     // private
31470     refreshValue: function() {
31471          var val = [];
31472         this.grid.getDataSource().each(function(r) {
31473             val.push(r.data);
31474         });
31475         this.el.dom.value = Roo.encode(val);
31476     }
31477     
31478      
31479     
31480     
31481 });/*
31482  * Based on:
31483  * Ext JS Library 1.1.1
31484  * Copyright(c) 2006-2007, Ext JS, LLC.
31485  *
31486  * Originally Released Under LGPL - original licence link has changed is not relivant.
31487  *
31488  * Fork - LGPL
31489  * <script type="text/javascript">
31490  */
31491 /**
31492  * @class Roo.form.DisplayField
31493  * @extends Roo.form.Field
31494  * A generic Field to display non-editable data.
31495  * @cfg {Boolean} closable (true|false) default false
31496  * @constructor
31497  * Creates a new Display Field item.
31498  * @param {Object} config Configuration options
31499  */
31500 Roo.form.DisplayField = function(config){
31501     Roo.form.DisplayField.superclass.constructor.call(this, config);
31502     
31503     this.addEvents({
31504         /**
31505          * @event close
31506          * Fires after the click the close btn
31507              * @param {Roo.form.DisplayField} this
31508              */
31509         close : true
31510     });
31511 };
31512
31513 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
31514     inputType:      'hidden',
31515     allowBlank:     true,
31516     readOnly:         true,
31517     
31518  
31519     /**
31520      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31521      */
31522     focusClass : undefined,
31523     /**
31524      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31525      */
31526     fieldClass: 'x-form-field',
31527     
31528      /**
31529      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31530      */
31531     valueRenderer: undefined,
31532     
31533     width: 100,
31534     /**
31535      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31536      * {tag: "input", type: "checkbox", autocomplete: "off"})
31537      */
31538      
31539  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31540  
31541     closable : false,
31542     
31543     onResize : function(){
31544         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31545         
31546     },
31547
31548     initEvents : function(){
31549         // Roo.form.Checkbox.superclass.initEvents.call(this);
31550         // has no events...
31551         
31552         if(this.closable){
31553             this.closeEl.on('click', this.onClose, this);
31554         }
31555        
31556     },
31557
31558
31559     getResizeEl : function(){
31560         return this.wrap;
31561     },
31562
31563     getPositionEl : function(){
31564         return this.wrap;
31565     },
31566
31567     // private
31568     onRender : function(ct, position){
31569         
31570         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31571         //if(this.inputValue !== undefined){
31572         this.wrap = this.el.wrap();
31573         
31574         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31575         
31576         if(this.closable){
31577             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31578         }
31579         
31580         if (this.bodyStyle) {
31581             this.viewEl.applyStyles(this.bodyStyle);
31582         }
31583         //this.viewEl.setStyle('padding', '2px');
31584         
31585         this.setValue(this.value);
31586         
31587     },
31588 /*
31589     // private
31590     initValue : Roo.emptyFn,
31591
31592   */
31593
31594         // private
31595     onClick : function(){
31596         
31597     },
31598
31599     /**
31600      * Sets the checked state of the checkbox.
31601      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31602      */
31603     setValue : function(v){
31604         this.value = v;
31605         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31606         // this might be called before we have a dom element..
31607         if (!this.viewEl) {
31608             return;
31609         }
31610         this.viewEl.dom.innerHTML = html;
31611         Roo.form.DisplayField.superclass.setValue.call(this, v);
31612
31613     },
31614     
31615     onClose : function(e)
31616     {
31617         e.preventDefault();
31618         
31619         this.fireEvent('close', this);
31620     }
31621 });/*
31622  * 
31623  * Licence- LGPL
31624  * 
31625  */
31626
31627 /**
31628  * @class Roo.form.DayPicker
31629  * @extends Roo.form.Field
31630  * A Day picker show [M] [T] [W] ....
31631  * @constructor
31632  * Creates a new Day Picker
31633  * @param {Object} config Configuration options
31634  */
31635 Roo.form.DayPicker= function(config){
31636     Roo.form.DayPicker.superclass.constructor.call(this, config);
31637      
31638 };
31639
31640 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31641     /**
31642      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31643      */
31644     focusClass : undefined,
31645     /**
31646      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31647      */
31648     fieldClass: "x-form-field",
31649    
31650     /**
31651      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31652      * {tag: "input", type: "checkbox", autocomplete: "off"})
31653      */
31654     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31655     
31656    
31657     actionMode : 'viewEl', 
31658     //
31659     // private
31660  
31661     inputType : 'hidden',
31662     
31663      
31664     inputElement: false, // real input element?
31665     basedOn: false, // ????
31666     
31667     isFormField: true, // not sure where this is needed!!!!
31668
31669     onResize : function(){
31670         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31671         if(!this.boxLabel){
31672             this.el.alignTo(this.wrap, 'c-c');
31673         }
31674     },
31675
31676     initEvents : function(){
31677         Roo.form.Checkbox.superclass.initEvents.call(this);
31678         this.el.on("click", this.onClick,  this);
31679         this.el.on("change", this.onClick,  this);
31680     },
31681
31682
31683     getResizeEl : function(){
31684         return this.wrap;
31685     },
31686
31687     getPositionEl : function(){
31688         return this.wrap;
31689     },
31690
31691     
31692     // private
31693     onRender : function(ct, position){
31694         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31695        
31696         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31697         
31698         var r1 = '<table><tr>';
31699         var r2 = '<tr class="x-form-daypick-icons">';
31700         for (var i=0; i < 7; i++) {
31701             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31702             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31703         }
31704         
31705         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31706         viewEl.select('img').on('click', this.onClick, this);
31707         this.viewEl = viewEl;   
31708         
31709         
31710         // this will not work on Chrome!!!
31711         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31712         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31713         
31714         
31715           
31716
31717     },
31718
31719     // private
31720     initValue : Roo.emptyFn,
31721
31722     /**
31723      * Returns the checked state of the checkbox.
31724      * @return {Boolean} True if checked, else false
31725      */
31726     getValue : function(){
31727         return this.el.dom.value;
31728         
31729     },
31730
31731         // private
31732     onClick : function(e){ 
31733         //this.setChecked(!this.checked);
31734         Roo.get(e.target).toggleClass('x-menu-item-checked');
31735         this.refreshValue();
31736         //if(this.el.dom.checked != this.checked){
31737         //    this.setValue(this.el.dom.checked);
31738        // }
31739     },
31740     
31741     // private
31742     refreshValue : function()
31743     {
31744         var val = '';
31745         this.viewEl.select('img',true).each(function(e,i,n)  {
31746             val += e.is(".x-menu-item-checked") ? String(n) : '';
31747         });
31748         this.setValue(val, true);
31749     },
31750
31751     /**
31752      * Sets the checked state of the checkbox.
31753      * On is always based on a string comparison between inputValue and the param.
31754      * @param {Boolean/String} value - the value to set 
31755      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31756      */
31757     setValue : function(v,suppressEvent){
31758         if (!this.el.dom) {
31759             return;
31760         }
31761         var old = this.el.dom.value ;
31762         this.el.dom.value = v;
31763         if (suppressEvent) {
31764             return ;
31765         }
31766          
31767         // update display..
31768         this.viewEl.select('img',true).each(function(e,i,n)  {
31769             
31770             var on = e.is(".x-menu-item-checked");
31771             var newv = v.indexOf(String(n)) > -1;
31772             if (on != newv) {
31773                 e.toggleClass('x-menu-item-checked');
31774             }
31775             
31776         });
31777         
31778         
31779         this.fireEvent('change', this, v, old);
31780         
31781         
31782     },
31783    
31784     // handle setting of hidden value by some other method!!?!?
31785     setFromHidden: function()
31786     {
31787         if(!this.el){
31788             return;
31789         }
31790         //console.log("SET FROM HIDDEN");
31791         //alert('setFrom hidden');
31792         this.setValue(this.el.dom.value);
31793     },
31794     
31795     onDestroy : function()
31796     {
31797         if(this.viewEl){
31798             Roo.get(this.viewEl).remove();
31799         }
31800          
31801         Roo.form.DayPicker.superclass.onDestroy.call(this);
31802     }
31803
31804 });/*
31805  * RooJS Library 1.1.1
31806  * Copyright(c) 2008-2011  Alan Knowles
31807  *
31808  * License - LGPL
31809  */
31810  
31811
31812 /**
31813  * @class Roo.form.ComboCheck
31814  * @extends Roo.form.ComboBox
31815  * A combobox for multiple select items.
31816  *
31817  * FIXME - could do with a reset button..
31818  * 
31819  * @constructor
31820  * Create a new ComboCheck
31821  * @param {Object} config Configuration options
31822  */
31823 Roo.form.ComboCheck = function(config){
31824     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31825     // should verify some data...
31826     // like
31827     // hiddenName = required..
31828     // displayField = required
31829     // valudField == required
31830     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31831     var _t = this;
31832     Roo.each(req, function(e) {
31833         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31834             throw "Roo.form.ComboCheck : missing value for: " + e;
31835         }
31836     });
31837     
31838     
31839 };
31840
31841 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31842      
31843      
31844     editable : false,
31845      
31846     selectedClass: 'x-menu-item-checked', 
31847     
31848     // private
31849     onRender : function(ct, position){
31850         var _t = this;
31851         
31852         
31853         
31854         if(!this.tpl){
31855             var cls = 'x-combo-list';
31856
31857             
31858             this.tpl =  new Roo.Template({
31859                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31860                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31861                    '<span>{' + this.displayField + '}</span>' +
31862                     '</div>' 
31863                 
31864             });
31865         }
31866  
31867         
31868         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31869         this.view.singleSelect = false;
31870         this.view.multiSelect = true;
31871         this.view.toggleSelect = true;
31872         this.pageTb.add(new Roo.Toolbar.Fill(), {
31873             
31874             text: 'Done',
31875             handler: function()
31876             {
31877                 _t.collapse();
31878             }
31879         });
31880     },
31881     
31882     onViewOver : function(e, t){
31883         // do nothing...
31884         return;
31885         
31886     },
31887     
31888     onViewClick : function(doFocus,index){
31889         return;
31890         
31891     },
31892     select: function () {
31893         //Roo.log("SELECT CALLED");
31894     },
31895      
31896     selectByValue : function(xv, scrollIntoView){
31897         var ar = this.getValueArray();
31898         var sels = [];
31899         
31900         Roo.each(ar, function(v) {
31901             if(v === undefined || v === null){
31902                 return;
31903             }
31904             var r = this.findRecord(this.valueField, v);
31905             if(r){
31906                 sels.push(this.store.indexOf(r))
31907                 
31908             }
31909         },this);
31910         this.view.select(sels);
31911         return false;
31912     },
31913     
31914     
31915     
31916     onSelect : function(record, index){
31917        // Roo.log("onselect Called");
31918        // this is only called by the clear button now..
31919         this.view.clearSelections();
31920         this.setValue('[]');
31921         if (this.value != this.valueBefore) {
31922             this.fireEvent('change', this, this.value, this.valueBefore);
31923             this.valueBefore = this.value;
31924         }
31925     },
31926     getValueArray : function()
31927     {
31928         var ar = [] ;
31929         
31930         try {
31931             //Roo.log(this.value);
31932             if (typeof(this.value) == 'undefined') {
31933                 return [];
31934             }
31935             var ar = Roo.decode(this.value);
31936             return  ar instanceof Array ? ar : []; //?? valid?
31937             
31938         } catch(e) {
31939             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31940             return [];
31941         }
31942          
31943     },
31944     expand : function ()
31945     {
31946         
31947         Roo.form.ComboCheck.superclass.expand.call(this);
31948         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31949         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31950         
31951
31952     },
31953     
31954     collapse : function(){
31955         Roo.form.ComboCheck.superclass.collapse.call(this);
31956         var sl = this.view.getSelectedIndexes();
31957         var st = this.store;
31958         var nv = [];
31959         var tv = [];
31960         var r;
31961         Roo.each(sl, function(i) {
31962             r = st.getAt(i);
31963             nv.push(r.get(this.valueField));
31964         },this);
31965         this.setValue(Roo.encode(nv));
31966         if (this.value != this.valueBefore) {
31967
31968             this.fireEvent('change', this, this.value, this.valueBefore);
31969             this.valueBefore = this.value;
31970         }
31971         
31972     },
31973     
31974     setValue : function(v){
31975         // Roo.log(v);
31976         this.value = v;
31977         
31978         var vals = this.getValueArray();
31979         var tv = [];
31980         Roo.each(vals, function(k) {
31981             var r = this.findRecord(this.valueField, k);
31982             if(r){
31983                 tv.push(r.data[this.displayField]);
31984             }else if(this.valueNotFoundText !== undefined){
31985                 tv.push( this.valueNotFoundText );
31986             }
31987         },this);
31988        // Roo.log(tv);
31989         
31990         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31991         this.hiddenField.value = v;
31992         this.value = v;
31993     }
31994     
31995 });/*
31996  * Based on:
31997  * Ext JS Library 1.1.1
31998  * Copyright(c) 2006-2007, Ext JS, LLC.
31999  *
32000  * Originally Released Under LGPL - original licence link has changed is not relivant.
32001  *
32002  * Fork - LGPL
32003  * <script type="text/javascript">
32004  */
32005  
32006 /**
32007  * @class Roo.form.Signature
32008  * @extends Roo.form.Field
32009  * Signature field.  
32010  * @constructor
32011  * 
32012  * @param {Object} config Configuration options
32013  */
32014
32015 Roo.form.Signature = function(config){
32016     Roo.form.Signature.superclass.constructor.call(this, config);
32017     
32018     this.addEvents({// not in used??
32019          /**
32020          * @event confirm
32021          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
32022              * @param {Roo.form.Signature} combo This combo box
32023              */
32024         'confirm' : true,
32025         /**
32026          * @event reset
32027          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
32028              * @param {Roo.form.ComboBox} combo This combo box
32029              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
32030              */
32031         'reset' : true
32032     });
32033 };
32034
32035 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
32036     /**
32037      * @cfg {Object} labels Label to use when rendering a form.
32038      * defaults to 
32039      * labels : { 
32040      *      clear : "Clear",
32041      *      confirm : "Confirm"
32042      *  }
32043      */
32044     labels : { 
32045         clear : "Clear",
32046         confirm : "Confirm"
32047     },
32048     /**
32049      * @cfg {Number} width The signature panel width (defaults to 300)
32050      */
32051     width: 300,
32052     /**
32053      * @cfg {Number} height The signature panel height (defaults to 100)
32054      */
32055     height : 100,
32056     /**
32057      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32058      */
32059     allowBlank : false,
32060     
32061     //private
32062     // {Object} signPanel The signature SVG panel element (defaults to {})
32063     signPanel : {},
32064     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32065     isMouseDown : false,
32066     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32067     isConfirmed : false,
32068     // {String} signatureTmp SVG mapping string (defaults to empty string)
32069     signatureTmp : '',
32070     
32071     
32072     defaultAutoCreate : { // modified by initCompnoent..
32073         tag: "input",
32074         type:"hidden"
32075     },
32076
32077     // private
32078     onRender : function(ct, position){
32079         
32080         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32081         
32082         this.wrap = this.el.wrap({
32083             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32084         });
32085         
32086         this.createToolbar(this);
32087         this.signPanel = this.wrap.createChild({
32088                 tag: 'div',
32089                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32090             }, this.el
32091         );
32092             
32093         this.svgID = Roo.id();
32094         this.svgEl = this.signPanel.createChild({
32095               xmlns : 'http://www.w3.org/2000/svg',
32096               tag : 'svg',
32097               id : this.svgID + "-svg",
32098               width: this.width,
32099               height: this.height,
32100               viewBox: '0 0 '+this.width+' '+this.height,
32101               cn : [
32102                 {
32103                     tag: "rect",
32104                     id: this.svgID + "-svg-r",
32105                     width: this.width,
32106                     height: this.height,
32107                     fill: "#ffa"
32108                 },
32109                 {
32110                     tag: "line",
32111                     id: this.svgID + "-svg-l",
32112                     x1: "0", // start
32113                     y1: (this.height*0.8), // start set the line in 80% of height
32114                     x2: this.width, // end
32115                     y2: (this.height*0.8), // end set the line in 80% of height
32116                     'stroke': "#666",
32117                     'stroke-width': "1",
32118                     'stroke-dasharray': "3",
32119                     'shape-rendering': "crispEdges",
32120                     'pointer-events': "none"
32121                 },
32122                 {
32123                     tag: "path",
32124                     id: this.svgID + "-svg-p",
32125                     'stroke': "navy",
32126                     'stroke-width': "3",
32127                     'fill': "none",
32128                     'pointer-events': 'none'
32129                 }
32130               ]
32131         });
32132         this.createSVG();
32133         this.svgBox = this.svgEl.dom.getScreenCTM();
32134     },
32135     createSVG : function(){ 
32136         var svg = this.signPanel;
32137         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32138         var t = this;
32139
32140         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32141         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32142         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32143         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32144         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32145         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32146         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32147         
32148     },
32149     isTouchEvent : function(e){
32150         return e.type.match(/^touch/);
32151     },
32152     getCoords : function (e) {
32153         var pt    = this.svgEl.dom.createSVGPoint();
32154         pt.x = e.clientX; 
32155         pt.y = e.clientY;
32156         if (this.isTouchEvent(e)) {
32157             pt.x =  e.targetTouches[0].clientX;
32158             pt.y = e.targetTouches[0].clientY;
32159         }
32160         var a = this.svgEl.dom.getScreenCTM();
32161         var b = a.inverse();
32162         var mx = pt.matrixTransform(b);
32163         return mx.x + ',' + mx.y;
32164     },
32165     //mouse event headler 
32166     down : function (e) {
32167         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32168         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32169         
32170         this.isMouseDown = true;
32171         
32172         e.preventDefault();
32173     },
32174     move : function (e) {
32175         if (this.isMouseDown) {
32176             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32177             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32178         }
32179         
32180         e.preventDefault();
32181     },
32182     up : function (e) {
32183         this.isMouseDown = false;
32184         var sp = this.signatureTmp.split(' ');
32185         
32186         if(sp.length > 1){
32187             if(!sp[sp.length-2].match(/^L/)){
32188                 sp.pop();
32189                 sp.pop();
32190                 sp.push("");
32191                 this.signatureTmp = sp.join(" ");
32192             }
32193         }
32194         if(this.getValue() != this.signatureTmp){
32195             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32196             this.isConfirmed = false;
32197         }
32198         e.preventDefault();
32199     },
32200     
32201     /**
32202      * Protected method that will not generally be called directly. It
32203      * is called when the editor creates its toolbar. Override this method if you need to
32204      * add custom toolbar buttons.
32205      * @param {HtmlEditor} editor
32206      */
32207     createToolbar : function(editor){
32208          function btn(id, toggle, handler){
32209             var xid = fid + '-'+ id ;
32210             return {
32211                 id : xid,
32212                 cmd : id,
32213                 cls : 'x-btn-icon x-edit-'+id,
32214                 enableToggle:toggle !== false,
32215                 scope: editor, // was editor...
32216                 handler:handler||editor.relayBtnCmd,
32217                 clickEvent:'mousedown',
32218                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32219                 tabIndex:-1
32220             };
32221         }
32222         
32223         
32224         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32225         this.tb = tb;
32226         this.tb.add(
32227            {
32228                 cls : ' x-signature-btn x-signature-'+id,
32229                 scope: editor, // was editor...
32230                 handler: this.reset,
32231                 clickEvent:'mousedown',
32232                 text: this.labels.clear
32233             },
32234             {
32235                  xtype : 'Fill',
32236                  xns: Roo.Toolbar
32237             }, 
32238             {
32239                 cls : '  x-signature-btn x-signature-'+id,
32240                 scope: editor, // was editor...
32241                 handler: this.confirmHandler,
32242                 clickEvent:'mousedown',
32243                 text: this.labels.confirm
32244             }
32245         );
32246     
32247     },
32248     //public
32249     /**
32250      * when user is clicked confirm then show this image.....
32251      * 
32252      * @return {String} Image Data URI
32253      */
32254     getImageDataURI : function(){
32255         var svg = this.svgEl.dom.parentNode.innerHTML;
32256         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32257         return src; 
32258     },
32259     /**
32260      * 
32261      * @return {Boolean} this.isConfirmed
32262      */
32263     getConfirmed : function(){
32264         return this.isConfirmed;
32265     },
32266     /**
32267      * 
32268      * @return {Number} this.width
32269      */
32270     getWidth : function(){
32271         return this.width;
32272     },
32273     /**
32274      * 
32275      * @return {Number} this.height
32276      */
32277     getHeight : function(){
32278         return this.height;
32279     },
32280     // private
32281     getSignature : function(){
32282         return this.signatureTmp;
32283     },
32284     // private
32285     reset : function(){
32286         this.signatureTmp = '';
32287         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32288         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32289         this.isConfirmed = false;
32290         Roo.form.Signature.superclass.reset.call(this);
32291     },
32292     setSignature : function(s){
32293         this.signatureTmp = s;
32294         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32295         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32296         this.setValue(s);
32297         this.isConfirmed = false;
32298         Roo.form.Signature.superclass.reset.call(this);
32299     }, 
32300     test : function(){
32301 //        Roo.log(this.signPanel.dom.contentWindow.up())
32302     },
32303     //private
32304     setConfirmed : function(){
32305         
32306         
32307         
32308 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32309     },
32310     // private
32311     confirmHandler : function(){
32312         if(!this.getSignature()){
32313             return;
32314         }
32315         
32316         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32317         this.setValue(this.getSignature());
32318         this.isConfirmed = true;
32319         
32320         this.fireEvent('confirm', this);
32321     },
32322     // private
32323     // Subclasses should provide the validation implementation by overriding this
32324     validateValue : function(value){
32325         if(this.allowBlank){
32326             return true;
32327         }
32328         
32329         if(this.isConfirmed){
32330             return true;
32331         }
32332         return false;
32333     }
32334 });/*
32335  * Based on:
32336  * Ext JS Library 1.1.1
32337  * Copyright(c) 2006-2007, Ext JS, LLC.
32338  *
32339  * Originally Released Under LGPL - original licence link has changed is not relivant.
32340  *
32341  * Fork - LGPL
32342  * <script type="text/javascript">
32343  */
32344  
32345
32346 /**
32347  * @class Roo.form.ComboBox
32348  * @extends Roo.form.TriggerField
32349  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32350  * @constructor
32351  * Create a new ComboBox.
32352  * @param {Object} config Configuration options
32353  */
32354 Roo.form.Select = function(config){
32355     Roo.form.Select.superclass.constructor.call(this, config);
32356      
32357 };
32358
32359 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32360     /**
32361      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32362      */
32363     /**
32364      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32365      * rendering into an Roo.Editor, defaults to false)
32366      */
32367     /**
32368      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32369      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32370      */
32371     /**
32372      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32373      */
32374     /**
32375      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32376      * the dropdown list (defaults to undefined, with no header element)
32377      */
32378
32379      /**
32380      * @cfg {String/Roo.Template} tpl The template to use to render the output
32381      */
32382      
32383     // private
32384     defaultAutoCreate : {tag: "select"  },
32385     /**
32386      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32387      */
32388     listWidth: undefined,
32389     /**
32390      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32391      * mode = 'remote' or 'text' if mode = 'local')
32392      */
32393     displayField: undefined,
32394     /**
32395      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32396      * mode = 'remote' or 'value' if mode = 'local'). 
32397      * Note: use of a valueField requires the user make a selection
32398      * in order for a value to be mapped.
32399      */
32400     valueField: undefined,
32401     
32402     
32403     /**
32404      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32405      * field's data value (defaults to the underlying DOM element's name)
32406      */
32407     hiddenName: undefined,
32408     /**
32409      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32410      */
32411     listClass: '',
32412     /**
32413      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32414      */
32415     selectedClass: 'x-combo-selected',
32416     /**
32417      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32418      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32419      * which displays a downward arrow icon).
32420      */
32421     triggerClass : 'x-form-arrow-trigger',
32422     /**
32423      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32424      */
32425     shadow:'sides',
32426     /**
32427      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32428      * anchor positions (defaults to 'tl-bl')
32429      */
32430     listAlign: 'tl-bl?',
32431     /**
32432      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32433      */
32434     maxHeight: 300,
32435     /**
32436      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
32437      * query specified by the allQuery config option (defaults to 'query')
32438      */
32439     triggerAction: 'query',
32440     /**
32441      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32442      * (defaults to 4, does not apply if editable = false)
32443      */
32444     minChars : 4,
32445     /**
32446      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32447      * delay (typeAheadDelay) if it matches a known value (defaults to false)
32448      */
32449     typeAhead: false,
32450     /**
32451      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32452      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32453      */
32454     queryDelay: 500,
32455     /**
32456      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32457      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
32458      */
32459     pageSize: 0,
32460     /**
32461      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
32462      * when editable = true (defaults to false)
32463      */
32464     selectOnFocus:false,
32465     /**
32466      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32467      */
32468     queryParam: 'query',
32469     /**
32470      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
32471      * when mode = 'remote' (defaults to 'Loading...')
32472      */
32473     loadingText: 'Loading...',
32474     /**
32475      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32476      */
32477     resizable: false,
32478     /**
32479      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32480      */
32481     handleHeight : 8,
32482     /**
32483      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32484      * traditional select (defaults to true)
32485      */
32486     editable: true,
32487     /**
32488      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32489      */
32490     allQuery: '',
32491     /**
32492      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32493      */
32494     mode: 'remote',
32495     /**
32496      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32497      * listWidth has a higher value)
32498      */
32499     minListWidth : 70,
32500     /**
32501      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32502      * allow the user to set arbitrary text into the field (defaults to false)
32503      */
32504     forceSelection:false,
32505     /**
32506      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32507      * if typeAhead = true (defaults to 250)
32508      */
32509     typeAheadDelay : 250,
32510     /**
32511      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32512      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32513      */
32514     valueNotFoundText : undefined,
32515     
32516     /**
32517      * @cfg {String} defaultValue The value displayed after loading the store.
32518      */
32519     defaultValue: '',
32520     
32521     /**
32522      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32523      */
32524     blockFocus : false,
32525     
32526     /**
32527      * @cfg {Boolean} disableClear Disable showing of clear button.
32528      */
32529     disableClear : false,
32530     /**
32531      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
32532      */
32533     alwaysQuery : false,
32534     
32535     //private
32536     addicon : false,
32537     editicon: false,
32538     
32539     // element that contains real text value.. (when hidden is used..)
32540      
32541     // private
32542     onRender : function(ct, position){
32543         Roo.form.Field.prototype.onRender.call(this, ct, position);
32544         
32545         if(this.store){
32546             this.store.on('beforeload', this.onBeforeLoad, this);
32547             this.store.on('load', this.onLoad, this);
32548             this.store.on('loadexception', this.onLoadException, this);
32549             this.store.load({});
32550         }
32551         
32552         
32553         
32554     },
32555
32556     // private
32557     initEvents : function(){
32558         //Roo.form.ComboBox.superclass.initEvents.call(this);
32559  
32560     },
32561
32562     onDestroy : function(){
32563        
32564         if(this.store){
32565             this.store.un('beforeload', this.onBeforeLoad, this);
32566             this.store.un('load', this.onLoad, this);
32567             this.store.un('loadexception', this.onLoadException, this);
32568         }
32569         //Roo.form.ComboBox.superclass.onDestroy.call(this);
32570     },
32571
32572     // private
32573     fireKey : function(e){
32574         if(e.isNavKeyPress() && !this.list.isVisible()){
32575             this.fireEvent("specialkey", this, e);
32576         }
32577     },
32578
32579     // private
32580     onResize: function(w, h){
32581         
32582         return; 
32583     
32584         
32585     },
32586
32587     /**
32588      * Allow or prevent the user from directly editing the field text.  If false is passed,
32589      * the user will only be able to select from the items defined in the dropdown list.  This method
32590      * is the runtime equivalent of setting the 'editable' config option at config time.
32591      * @param {Boolean} value True to allow the user to directly edit the field text
32592      */
32593     setEditable : function(value){
32594          
32595     },
32596
32597     // private
32598     onBeforeLoad : function(){
32599         
32600         Roo.log("Select before load");
32601         return;
32602     
32603         this.innerList.update(this.loadingText ?
32604                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32605         //this.restrictHeight();
32606         this.selectedIndex = -1;
32607     },
32608
32609     // private
32610     onLoad : function(){
32611
32612     
32613         var dom = this.el.dom;
32614         dom.innerHTML = '';
32615          var od = dom.ownerDocument;
32616          
32617         if (this.emptyText) {
32618             var op = od.createElement('option');
32619             op.setAttribute('value', '');
32620             op.innerHTML = String.format('{0}', this.emptyText);
32621             dom.appendChild(op);
32622         }
32623         if(this.store.getCount() > 0){
32624            
32625             var vf = this.valueField;
32626             var df = this.displayField;
32627             this.store.data.each(function(r) {
32628                 // which colmsn to use... testing - cdoe / title..
32629                 var op = od.createElement('option');
32630                 op.setAttribute('value', r.data[vf]);
32631                 op.innerHTML = String.format('{0}', r.data[df]);
32632                 dom.appendChild(op);
32633             });
32634             if (typeof(this.defaultValue != 'undefined')) {
32635                 this.setValue(this.defaultValue);
32636             }
32637             
32638              
32639         }else{
32640             //this.onEmptyResults();
32641         }
32642         //this.el.focus();
32643     },
32644     // private
32645     onLoadException : function()
32646     {
32647         dom.innerHTML = '';
32648             
32649         Roo.log("Select on load exception");
32650         return;
32651     
32652         this.collapse();
32653         Roo.log(this.store.reader.jsonData);
32654         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32655             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32656         }
32657         
32658         
32659     },
32660     // private
32661     onTypeAhead : function(){
32662          
32663     },
32664
32665     // private
32666     onSelect : function(record, index){
32667         Roo.log('on select?');
32668         return;
32669         if(this.fireEvent('beforeselect', this, record, index) !== false){
32670             this.setFromData(index > -1 ? record.data : false);
32671             this.collapse();
32672             this.fireEvent('select', this, record, index);
32673         }
32674     },
32675
32676     /**
32677      * Returns the currently selected field value or empty string if no value is set.
32678      * @return {String} value The selected value
32679      */
32680     getValue : function(){
32681         var dom = this.el.dom;
32682         this.value = dom.options[dom.selectedIndex].value;
32683         return this.value;
32684         
32685     },
32686
32687     /**
32688      * Clears any text/value currently set in the field
32689      */
32690     clearValue : function(){
32691         this.value = '';
32692         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32693         
32694     },
32695
32696     /**
32697      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32698      * will be displayed in the field.  If the value does not match the data value of an existing item,
32699      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32700      * Otherwise the field will be blank (although the value will still be set).
32701      * @param {String} value The value to match
32702      */
32703     setValue : function(v){
32704         var d = this.el.dom;
32705         for (var i =0; i < d.options.length;i++) {
32706             if (v == d.options[i].value) {
32707                 d.selectedIndex = i;
32708                 this.value = v;
32709                 return;
32710             }
32711         }
32712         this.clearValue();
32713     },
32714     /**
32715      * @property {Object} the last set data for the element
32716      */
32717     
32718     lastData : false,
32719     /**
32720      * Sets the value of the field based on a object which is related to the record format for the store.
32721      * @param {Object} value the value to set as. or false on reset?
32722      */
32723     setFromData : function(o){
32724         Roo.log('setfrom data?');
32725          
32726         
32727         
32728     },
32729     // private
32730     reset : function(){
32731         this.clearValue();
32732     },
32733     // private
32734     findRecord : function(prop, value){
32735         
32736         return false;
32737     
32738         var record;
32739         if(this.store.getCount() > 0){
32740             this.store.each(function(r){
32741                 if(r.data[prop] == value){
32742                     record = r;
32743                     return false;
32744                 }
32745                 return true;
32746             });
32747         }
32748         return record;
32749     },
32750     
32751     getName: function()
32752     {
32753         // returns hidden if it's set..
32754         if (!this.rendered) {return ''};
32755         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32756         
32757     },
32758      
32759
32760     
32761
32762     // private
32763     onEmptyResults : function(){
32764         Roo.log('empty results');
32765         //this.collapse();
32766     },
32767
32768     /**
32769      * Returns true if the dropdown list is expanded, else false.
32770      */
32771     isExpanded : function(){
32772         return false;
32773     },
32774
32775     /**
32776      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32777      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32778      * @param {String} value The data value of the item to select
32779      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32780      * selected item if it is not currently in view (defaults to true)
32781      * @return {Boolean} True if the value matched an item in the list, else false
32782      */
32783     selectByValue : function(v, scrollIntoView){
32784         Roo.log('select By Value');
32785         return false;
32786     
32787         if(v !== undefined && v !== null){
32788             var r = this.findRecord(this.valueField || this.displayField, v);
32789             if(r){
32790                 this.select(this.store.indexOf(r), scrollIntoView);
32791                 return true;
32792             }
32793         }
32794         return false;
32795     },
32796
32797     /**
32798      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32799      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32800      * @param {Number} index The zero-based index of the list item to select
32801      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32802      * selected item if it is not currently in view (defaults to true)
32803      */
32804     select : function(index, scrollIntoView){
32805         Roo.log('select ');
32806         return  ;
32807         
32808         this.selectedIndex = index;
32809         this.view.select(index);
32810         if(scrollIntoView !== false){
32811             var el = this.view.getNode(index);
32812             if(el){
32813                 this.innerList.scrollChildIntoView(el, false);
32814             }
32815         }
32816     },
32817
32818       
32819
32820     // private
32821     validateBlur : function(){
32822         
32823         return;
32824         
32825     },
32826
32827     // private
32828     initQuery : function(){
32829         this.doQuery(this.getRawValue());
32830     },
32831
32832     // private
32833     doForce : function(){
32834         if(this.el.dom.value.length > 0){
32835             this.el.dom.value =
32836                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32837              
32838         }
32839     },
32840
32841     /**
32842      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32843      * query allowing the query action to be canceled if needed.
32844      * @param {String} query The SQL query to execute
32845      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32846      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32847      * saved in the current store (defaults to false)
32848      */
32849     doQuery : function(q, forceAll){
32850         
32851         Roo.log('doQuery?');
32852         if(q === undefined || q === null){
32853             q = '';
32854         }
32855         var qe = {
32856             query: q,
32857             forceAll: forceAll,
32858             combo: this,
32859             cancel:false
32860         };
32861         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32862             return false;
32863         }
32864         q = qe.query;
32865         forceAll = qe.forceAll;
32866         if(forceAll === true || (q.length >= this.minChars)){
32867             if(this.lastQuery != q || this.alwaysQuery){
32868                 this.lastQuery = q;
32869                 if(this.mode == 'local'){
32870                     this.selectedIndex = -1;
32871                     if(forceAll){
32872                         this.store.clearFilter();
32873                     }else{
32874                         this.store.filter(this.displayField, q);
32875                     }
32876                     this.onLoad();
32877                 }else{
32878                     this.store.baseParams[this.queryParam] = q;
32879                     this.store.load({
32880                         params: this.getParams(q)
32881                     });
32882                     this.expand();
32883                 }
32884             }else{
32885                 this.selectedIndex = -1;
32886                 this.onLoad();   
32887             }
32888         }
32889     },
32890
32891     // private
32892     getParams : function(q){
32893         var p = {};
32894         //p[this.queryParam] = q;
32895         if(this.pageSize){
32896             p.start = 0;
32897             p.limit = this.pageSize;
32898         }
32899         return p;
32900     },
32901
32902     /**
32903      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32904      */
32905     collapse : function(){
32906         
32907     },
32908
32909     // private
32910     collapseIf : function(e){
32911         
32912     },
32913
32914     /**
32915      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32916      */
32917     expand : function(){
32918         
32919     } ,
32920
32921     // private
32922      
32923
32924     /** 
32925     * @cfg {Boolean} grow 
32926     * @hide 
32927     */
32928     /** 
32929     * @cfg {Number} growMin 
32930     * @hide 
32931     */
32932     /** 
32933     * @cfg {Number} growMax 
32934     * @hide 
32935     */
32936     /**
32937      * @hide
32938      * @method autoSize
32939      */
32940     
32941     setWidth : function()
32942     {
32943         
32944     },
32945     getResizeEl : function(){
32946         return this.el;
32947     }
32948 });//<script type="text/javasscript">
32949  
32950
32951 /**
32952  * @class Roo.DDView
32953  * A DnD enabled version of Roo.View.
32954  * @param {Element/String} container The Element in which to create the View.
32955  * @param {String} tpl The template string used to create the markup for each element of the View
32956  * @param {Object} config The configuration properties. These include all the config options of
32957  * {@link Roo.View} plus some specific to this class.<br>
32958  * <p>
32959  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32960  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32961  * <p>
32962  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32963 .x-view-drag-insert-above {
32964         border-top:1px dotted #3366cc;
32965 }
32966 .x-view-drag-insert-below {
32967         border-bottom:1px dotted #3366cc;
32968 }
32969 </code></pre>
32970  * 
32971  */
32972  
32973 Roo.DDView = function(container, tpl, config) {
32974     Roo.DDView.superclass.constructor.apply(this, arguments);
32975     this.getEl().setStyle("outline", "0px none");
32976     this.getEl().unselectable();
32977     if (this.dragGroup) {
32978         this.setDraggable(this.dragGroup.split(","));
32979     }
32980     if (this.dropGroup) {
32981         this.setDroppable(this.dropGroup.split(","));
32982     }
32983     if (this.deletable) {
32984         this.setDeletable();
32985     }
32986     this.isDirtyFlag = false;
32987         this.addEvents({
32988                 "drop" : true
32989         });
32990 };
32991
32992 Roo.extend(Roo.DDView, Roo.View, {
32993 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32994 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32995 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32996 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32997
32998         isFormField: true,
32999
33000         reset: Roo.emptyFn,
33001         
33002         clearInvalid: Roo.form.Field.prototype.clearInvalid,
33003
33004         validate: function() {
33005                 return true;
33006         },
33007         
33008         destroy: function() {
33009                 this.purgeListeners();
33010                 this.getEl.removeAllListeners();
33011                 this.getEl().remove();
33012                 if (this.dragZone) {
33013                         if (this.dragZone.destroy) {
33014                                 this.dragZone.destroy();
33015                         }
33016                 }
33017                 if (this.dropZone) {
33018                         if (this.dropZone.destroy) {
33019                                 this.dropZone.destroy();
33020                         }
33021                 }
33022         },
33023
33024 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
33025         getName: function() {
33026                 return this.name;
33027         },
33028
33029 /**     Loads the View from a JSON string representing the Records to put into the Store. */
33030         setValue: function(v) {
33031                 if (!this.store) {
33032                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
33033                 }
33034                 var data = {};
33035                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
33036                 this.store.proxy = new Roo.data.MemoryProxy(data);
33037                 this.store.load();
33038         },
33039
33040 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
33041         getValue: function() {
33042                 var result = '(';
33043                 this.store.each(function(rec) {
33044                         result += rec.id + ',';
33045                 });
33046                 return result.substr(0, result.length - 1) + ')';
33047         },
33048         
33049         getIds: function() {
33050                 var i = 0, result = new Array(this.store.getCount());
33051                 this.store.each(function(rec) {
33052                         result[i++] = rec.id;
33053                 });
33054                 return result;
33055         },
33056         
33057         isDirty: function() {
33058                 return this.isDirtyFlag;
33059         },
33060
33061 /**
33062  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
33063  *      whole Element becomes the target, and this causes the drop gesture to append.
33064  */
33065     getTargetFromEvent : function(e) {
33066                 var target = e.getTarget();
33067                 while ((target !== null) && (target.parentNode != this.el.dom)) {
33068                 target = target.parentNode;
33069                 }
33070                 if (!target) {
33071                         target = this.el.dom.lastChild || this.el.dom;
33072                 }
33073                 return target;
33074     },
33075
33076 /**
33077  *      Create the drag data which consists of an object which has the property "ddel" as
33078  *      the drag proxy element. 
33079  */
33080     getDragData : function(e) {
33081         var target = this.findItemFromChild(e.getTarget());
33082                 if(target) {
33083                         this.handleSelection(e);
33084                         var selNodes = this.getSelectedNodes();
33085             var dragData = {
33086                 source: this,
33087                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33088                 nodes: selNodes,
33089                 records: []
33090                         };
33091                         var selectedIndices = this.getSelectedIndexes();
33092                         for (var i = 0; i < selectedIndices.length; i++) {
33093                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33094                         }
33095                         if (selNodes.length == 1) {
33096                                 dragData.ddel = target.cloneNode(true); // the div element
33097                         } else {
33098                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33099                                 div.className = 'multi-proxy';
33100                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33101                                         div.appendChild(selNodes[i].cloneNode(true));
33102                                 }
33103                                 dragData.ddel = div;
33104                         }
33105             //console.log(dragData)
33106             //console.log(dragData.ddel.innerHTML)
33107                         return dragData;
33108                 }
33109         //console.log('nodragData')
33110                 return false;
33111     },
33112     
33113 /**     Specify to which ddGroup items in this DDView may be dragged. */
33114     setDraggable: function(ddGroup) {
33115         if (ddGroup instanceof Array) {
33116                 Roo.each(ddGroup, this.setDraggable, this);
33117                 return;
33118         }
33119         if (this.dragZone) {
33120                 this.dragZone.addToGroup(ddGroup);
33121         } else {
33122                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33123                                 containerScroll: true,
33124                                 ddGroup: ddGroup 
33125
33126                         });
33127 //                      Draggability implies selection. DragZone's mousedown selects the element.
33128                         if (!this.multiSelect) { this.singleSelect = true; }
33129
33130 //                      Wire the DragZone's handlers up to methods in *this*
33131                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33132                 }
33133     },
33134
33135 /**     Specify from which ddGroup this DDView accepts drops. */
33136     setDroppable: function(ddGroup) {
33137         if (ddGroup instanceof Array) {
33138                 Roo.each(ddGroup, this.setDroppable, this);
33139                 return;
33140         }
33141         if (this.dropZone) {
33142                 this.dropZone.addToGroup(ddGroup);
33143         } else {
33144                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33145                                 containerScroll: true,
33146                                 ddGroup: ddGroup
33147                         });
33148
33149 //                      Wire the DropZone's handlers up to methods in *this*
33150                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33151                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33152                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33153                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33154                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33155                 }
33156     },
33157
33158 /**     Decide whether to drop above or below a View node. */
33159     getDropPoint : function(e, n, dd){
33160         if (n == this.el.dom) { return "above"; }
33161                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33162                 var c = t + (b - t) / 2;
33163                 var y = Roo.lib.Event.getPageY(e);
33164                 if(y <= c) {
33165                         return "above";
33166                 }else{
33167                         return "below";
33168                 }
33169     },
33170
33171     onNodeEnter : function(n, dd, e, data){
33172                 return false;
33173     },
33174     
33175     onNodeOver : function(n, dd, e, data){
33176                 var pt = this.getDropPoint(e, n, dd);
33177                 // set the insert point style on the target node
33178                 var dragElClass = this.dropNotAllowed;
33179                 if (pt) {
33180                         var targetElClass;
33181                         if (pt == "above"){
33182                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33183                                 targetElClass = "x-view-drag-insert-above";
33184                         } else {
33185                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33186                                 targetElClass = "x-view-drag-insert-below";
33187                         }
33188                         if (this.lastInsertClass != targetElClass){
33189                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33190                                 this.lastInsertClass = targetElClass;
33191                         }
33192                 }
33193                 return dragElClass;
33194         },
33195
33196     onNodeOut : function(n, dd, e, data){
33197                 this.removeDropIndicators(n);
33198     },
33199
33200     onNodeDrop : function(n, dd, e, data){
33201         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33202                 return false;
33203         }
33204         var pt = this.getDropPoint(e, n, dd);
33205                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33206                 if (pt == "below") { insertAt++; }
33207                 for (var i = 0; i < data.records.length; i++) {
33208                         var r = data.records[i];
33209                         var dup = this.store.getById(r.id);
33210                         if (dup && (dd != this.dragZone)) {
33211                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33212                         } else {
33213                                 if (data.copy) {
33214                                         this.store.insert(insertAt++, r.copy());
33215                                 } else {
33216                                         data.source.isDirtyFlag = true;
33217                                         r.store.remove(r);
33218                                         this.store.insert(insertAt++, r);
33219                                 }
33220                                 this.isDirtyFlag = true;
33221                         }
33222                 }
33223                 this.dragZone.cachedTarget = null;
33224                 return true;
33225     },
33226
33227     removeDropIndicators : function(n){
33228                 if(n){
33229                         Roo.fly(n).removeClass([
33230                                 "x-view-drag-insert-above",
33231                                 "x-view-drag-insert-below"]);
33232                         this.lastInsertClass = "_noclass";
33233                 }
33234     },
33235
33236 /**
33237  *      Utility method. Add a delete option to the DDView's context menu.
33238  *      @param {String} imageUrl The URL of the "delete" icon image.
33239  */
33240         setDeletable: function(imageUrl) {
33241                 if (!this.singleSelect && !this.multiSelect) {
33242                         this.singleSelect = true;
33243                 }
33244                 var c = this.getContextMenu();
33245                 this.contextMenu.on("itemclick", function(item) {
33246                         switch (item.id) {
33247                                 case "delete":
33248                                         this.remove(this.getSelectedIndexes());
33249                                         break;
33250                         }
33251                 }, this);
33252                 this.contextMenu.add({
33253                         icon: imageUrl,
33254                         id: "delete",
33255                         text: 'Delete'
33256                 });
33257         },
33258         
33259 /**     Return the context menu for this DDView. */
33260         getContextMenu: function() {
33261                 if (!this.contextMenu) {
33262 //                      Create the View's context menu
33263                         this.contextMenu = new Roo.menu.Menu({
33264                                 id: this.id + "-contextmenu"
33265                         });
33266                         this.el.on("contextmenu", this.showContextMenu, this);
33267                 }
33268                 return this.contextMenu;
33269         },
33270         
33271         disableContextMenu: function() {
33272                 if (this.contextMenu) {
33273                         this.el.un("contextmenu", this.showContextMenu, this);
33274                 }
33275         },
33276
33277         showContextMenu: function(e, item) {
33278         item = this.findItemFromChild(e.getTarget());
33279                 if (item) {
33280                         e.stopEvent();
33281                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33282                         this.contextMenu.showAt(e.getXY());
33283             }
33284     },
33285
33286 /**
33287  *      Remove {@link Roo.data.Record}s at the specified indices.
33288  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33289  */
33290     remove: function(selectedIndices) {
33291                 selectedIndices = [].concat(selectedIndices);
33292                 for (var i = 0; i < selectedIndices.length; i++) {
33293                         var rec = this.store.getAt(selectedIndices[i]);
33294                         this.store.remove(rec);
33295                 }
33296     },
33297
33298 /**
33299  *      Double click fires the event, but also, if this is draggable, and there is only one other
33300  *      related DropZone, it transfers the selected node.
33301  */
33302     onDblClick : function(e){
33303         var item = this.findItemFromChild(e.getTarget());
33304         if(item){
33305             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33306                 return false;
33307             }
33308             if (this.dragGroup) {
33309                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33310                     while (targets.indexOf(this.dropZone) > -1) {
33311                             targets.remove(this.dropZone);
33312                                 }
33313                     if (targets.length == 1) {
33314                                         this.dragZone.cachedTarget = null;
33315                         var el = Roo.get(targets[0].getEl());
33316                         var box = el.getBox(true);
33317                         targets[0].onNodeDrop(el.dom, {
33318                                 target: el.dom,
33319                                 xy: [box.x, box.y + box.height - 1]
33320                         }, null, this.getDragData(e));
33321                     }
33322                 }
33323         }
33324     },
33325     
33326     handleSelection: function(e) {
33327                 this.dragZone.cachedTarget = null;
33328         var item = this.findItemFromChild(e.getTarget());
33329         if (!item) {
33330                 this.clearSelections(true);
33331                 return;
33332         }
33333                 if (item && (this.multiSelect || this.singleSelect)){
33334                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33335                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33336                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33337                                 this.unselect(item);
33338                         } else {
33339                                 this.select(item, this.multiSelect && e.ctrlKey);
33340                                 this.lastSelection = item;
33341                         }
33342                 }
33343     },
33344
33345     onItemClick : function(item, index, e){
33346                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33347                         return false;
33348                 }
33349                 return true;
33350     },
33351
33352     unselect : function(nodeInfo, suppressEvent){
33353                 var node = this.getNode(nodeInfo);
33354                 if(node && this.isSelected(node)){
33355                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33356                                 Roo.fly(node).removeClass(this.selectedClass);
33357                                 this.selections.remove(node);
33358                                 if(!suppressEvent){
33359                                         this.fireEvent("selectionchange", this, this.selections);
33360                                 }
33361                         }
33362                 }
33363     }
33364 });
33365 /*
33366  * Based on:
33367  * Ext JS Library 1.1.1
33368  * Copyright(c) 2006-2007, Ext JS, LLC.
33369  *
33370  * Originally Released Under LGPL - original licence link has changed is not relivant.
33371  *
33372  * Fork - LGPL
33373  * <script type="text/javascript">
33374  */
33375  
33376 /**
33377  * @class Roo.LayoutManager
33378  * @extends Roo.util.Observable
33379  * Base class for layout managers.
33380  */
33381 Roo.LayoutManager = function(container, config){
33382     Roo.LayoutManager.superclass.constructor.call(this);
33383     this.el = Roo.get(container);
33384     // ie scrollbar fix
33385     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33386         document.body.scroll = "no";
33387     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33388         this.el.position('relative');
33389     }
33390     this.id = this.el.id;
33391     this.el.addClass("x-layout-container");
33392     /** false to disable window resize monitoring @type Boolean */
33393     this.monitorWindowResize = true;
33394     this.regions = {};
33395     this.addEvents({
33396         /**
33397          * @event layout
33398          * Fires when a layout is performed. 
33399          * @param {Roo.LayoutManager} this
33400          */
33401         "layout" : true,
33402         /**
33403          * @event regionresized
33404          * Fires when the user resizes a region. 
33405          * @param {Roo.LayoutRegion} region The resized region
33406          * @param {Number} newSize The new size (width for east/west, height for north/south)
33407          */
33408         "regionresized" : true,
33409         /**
33410          * @event regioncollapsed
33411          * Fires when a region is collapsed. 
33412          * @param {Roo.LayoutRegion} region The collapsed region
33413          */
33414         "regioncollapsed" : true,
33415         /**
33416          * @event regionexpanded
33417          * Fires when a region is expanded.  
33418          * @param {Roo.LayoutRegion} region The expanded region
33419          */
33420         "regionexpanded" : true
33421     });
33422     this.updating = false;
33423     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33424 };
33425
33426 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33427     /**
33428      * Returns true if this layout is currently being updated
33429      * @return {Boolean}
33430      */
33431     isUpdating : function(){
33432         return this.updating; 
33433     },
33434     
33435     /**
33436      * Suspend the LayoutManager from doing auto-layouts while
33437      * making multiple add or remove calls
33438      */
33439     beginUpdate : function(){
33440         this.updating = true;    
33441     },
33442     
33443     /**
33444      * Restore auto-layouts and optionally disable the manager from performing a layout
33445      * @param {Boolean} noLayout true to disable a layout update 
33446      */
33447     endUpdate : function(noLayout){
33448         this.updating = false;
33449         if(!noLayout){
33450             this.layout();
33451         }    
33452     },
33453     
33454     layout: function(){
33455         
33456     },
33457     
33458     onRegionResized : function(region, newSize){
33459         this.fireEvent("regionresized", region, newSize);
33460         this.layout();
33461     },
33462     
33463     onRegionCollapsed : function(region){
33464         this.fireEvent("regioncollapsed", region);
33465     },
33466     
33467     onRegionExpanded : function(region){
33468         this.fireEvent("regionexpanded", region);
33469     },
33470         
33471     /**
33472      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33473      * performs box-model adjustments.
33474      * @return {Object} The size as an object {width: (the width), height: (the height)}
33475      */
33476     getViewSize : function(){
33477         var size;
33478         if(this.el.dom != document.body){
33479             size = this.el.getSize();
33480         }else{
33481             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33482         }
33483         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33484         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33485         return size;
33486     },
33487     
33488     /**
33489      * Returns the Element this layout is bound to.
33490      * @return {Roo.Element}
33491      */
33492     getEl : function(){
33493         return this.el;
33494     },
33495     
33496     /**
33497      * Returns the specified region.
33498      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33499      * @return {Roo.LayoutRegion}
33500      */
33501     getRegion : function(target){
33502         return this.regions[target.toLowerCase()];
33503     },
33504     
33505     onWindowResize : function(){
33506         if(this.monitorWindowResize){
33507             this.layout();
33508         }
33509     }
33510 });/*
33511  * Based on:
33512  * Ext JS Library 1.1.1
33513  * Copyright(c) 2006-2007, Ext JS, LLC.
33514  *
33515  * Originally Released Under LGPL - original licence link has changed is not relivant.
33516  *
33517  * Fork - LGPL
33518  * <script type="text/javascript">
33519  */
33520 /**
33521  * @class Roo.BorderLayout
33522  * @extends Roo.LayoutManager
33523  * @children Roo.ContentPanel
33524  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33525  * please see: <br><br>
33526  * <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>
33527  * <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>
33528  * Example:
33529  <pre><code>
33530  var layout = new Roo.BorderLayout(document.body, {
33531     north: {
33532         initialSize: 25,
33533         titlebar: false
33534     },
33535     west: {
33536         split:true,
33537         initialSize: 200,
33538         minSize: 175,
33539         maxSize: 400,
33540         titlebar: true,
33541         collapsible: true
33542     },
33543     east: {
33544         split:true,
33545         initialSize: 202,
33546         minSize: 175,
33547         maxSize: 400,
33548         titlebar: true,
33549         collapsible: true
33550     },
33551     south: {
33552         split:true,
33553         initialSize: 100,
33554         minSize: 100,
33555         maxSize: 200,
33556         titlebar: true,
33557         collapsible: true
33558     },
33559     center: {
33560         titlebar: true,
33561         autoScroll:true,
33562         resizeTabs: true,
33563         minTabWidth: 50,
33564         preferredTabWidth: 150
33565     }
33566 });
33567
33568 // shorthand
33569 var CP = Roo.ContentPanel;
33570
33571 layout.beginUpdate();
33572 layout.add("north", new CP("north", "North"));
33573 layout.add("south", new CP("south", {title: "South", closable: true}));
33574 layout.add("west", new CP("west", {title: "West"}));
33575 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33576 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33577 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33578 layout.getRegion("center").showPanel("center1");
33579 layout.endUpdate();
33580 </code></pre>
33581
33582 <b>The container the layout is rendered into can be either the body element or any other element.
33583 If it is not the body element, the container needs to either be an absolute positioned element,
33584 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33585 the container size if it is not the body element.</b>
33586
33587 * @constructor
33588 * Create a new BorderLayout
33589 * @param {String/HTMLElement/Element} container The container this layout is bound to
33590 * @param {Object} config Configuration options
33591  */
33592 Roo.BorderLayout = function(container, config){
33593     config = config || {};
33594     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33595     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33596     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33597         var target = this.factory.validRegions[i];
33598         if(config[target]){
33599             this.addRegion(target, config[target]);
33600         }
33601     }
33602 };
33603
33604 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33605         
33606         /**
33607          * @cfg {Roo.LayoutRegion} east
33608          */
33609         /**
33610          * @cfg {Roo.LayoutRegion} west
33611          */
33612         /**
33613          * @cfg {Roo.LayoutRegion} north
33614          */
33615         /**
33616          * @cfg {Roo.LayoutRegion} south
33617          */
33618         /**
33619          * @cfg {Roo.LayoutRegion} center
33620          */
33621     /**
33622      * Creates and adds a new region if it doesn't already exist.
33623      * @param {String} target The target region key (north, south, east, west or center).
33624      * @param {Object} config The regions config object
33625      * @return {BorderLayoutRegion} The new region
33626      */
33627     addRegion : function(target, config){
33628         if(!this.regions[target]){
33629             var r = this.factory.create(target, this, config);
33630             this.bindRegion(target, r);
33631         }
33632         return this.regions[target];
33633     },
33634
33635     // private (kinda)
33636     bindRegion : function(name, r){
33637         this.regions[name] = r;
33638         r.on("visibilitychange", this.layout, this);
33639         r.on("paneladded", this.layout, this);
33640         r.on("panelremoved", this.layout, this);
33641         r.on("invalidated", this.layout, this);
33642         r.on("resized", this.onRegionResized, this);
33643         r.on("collapsed", this.onRegionCollapsed, this);
33644         r.on("expanded", this.onRegionExpanded, this);
33645     },
33646
33647     /**
33648      * Performs a layout update.
33649      */
33650     layout : function(){
33651         if(this.updating) {
33652             return;
33653         }
33654         var size = this.getViewSize();
33655         var w = size.width;
33656         var h = size.height;
33657         var centerW = w;
33658         var centerH = h;
33659         var centerY = 0;
33660         var centerX = 0;
33661         //var x = 0, y = 0;
33662
33663         var rs = this.regions;
33664         var north = rs["north"];
33665         var south = rs["south"]; 
33666         var west = rs["west"];
33667         var east = rs["east"];
33668         var center = rs["center"];
33669         //if(this.hideOnLayout){ // not supported anymore
33670             //c.el.setStyle("display", "none");
33671         //}
33672         if(north && north.isVisible()){
33673             var b = north.getBox();
33674             var m = north.getMargins();
33675             b.width = w - (m.left+m.right);
33676             b.x = m.left;
33677             b.y = m.top;
33678             centerY = b.height + b.y + m.bottom;
33679             centerH -= centerY;
33680             north.updateBox(this.safeBox(b));
33681         }
33682         if(south && south.isVisible()){
33683             var b = south.getBox();
33684             var m = south.getMargins();
33685             b.width = w - (m.left+m.right);
33686             b.x = m.left;
33687             var totalHeight = (b.height + m.top + m.bottom);
33688             b.y = h - totalHeight + m.top;
33689             centerH -= totalHeight;
33690             south.updateBox(this.safeBox(b));
33691         }
33692         if(west && west.isVisible()){
33693             var b = west.getBox();
33694             var m = west.getMargins();
33695             b.height = centerH - (m.top+m.bottom);
33696             b.x = m.left;
33697             b.y = centerY + m.top;
33698             var totalWidth = (b.width + m.left + m.right);
33699             centerX += totalWidth;
33700             centerW -= totalWidth;
33701             west.updateBox(this.safeBox(b));
33702         }
33703         if(east && east.isVisible()){
33704             var b = east.getBox();
33705             var m = east.getMargins();
33706             b.height = centerH - (m.top+m.bottom);
33707             var totalWidth = (b.width + m.left + m.right);
33708             b.x = w - totalWidth + m.left;
33709             b.y = centerY + m.top;
33710             centerW -= totalWidth;
33711             east.updateBox(this.safeBox(b));
33712         }
33713         if(center){
33714             var m = center.getMargins();
33715             var centerBox = {
33716                 x: centerX + m.left,
33717                 y: centerY + m.top,
33718                 width: centerW - (m.left+m.right),
33719                 height: centerH - (m.top+m.bottom)
33720             };
33721             //if(this.hideOnLayout){
33722                 //center.el.setStyle("display", "block");
33723             //}
33724             center.updateBox(this.safeBox(centerBox));
33725         }
33726         this.el.repaint();
33727         this.fireEvent("layout", this);
33728     },
33729
33730     // private
33731     safeBox : function(box){
33732         box.width = Math.max(0, box.width);
33733         box.height = Math.max(0, box.height);
33734         return box;
33735     },
33736
33737     /**
33738      * Adds a ContentPanel (or subclass) to this layout.
33739      * @param {String} target The target region key (north, south, east, west or center).
33740      * @param {Roo.ContentPanel} panel The panel to add
33741      * @return {Roo.ContentPanel} The added panel
33742      */
33743     add : function(target, panel){
33744          
33745         target = target.toLowerCase();
33746         return this.regions[target].add(panel);
33747     },
33748
33749     /**
33750      * Remove a ContentPanel (or subclass) to this layout.
33751      * @param {String} target The target region key (north, south, east, west or center).
33752      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33753      * @return {Roo.ContentPanel} The removed panel
33754      */
33755     remove : function(target, panel){
33756         target = target.toLowerCase();
33757         return this.regions[target].remove(panel);
33758     },
33759
33760     /**
33761      * Searches all regions for a panel with the specified id
33762      * @param {String} panelId
33763      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33764      */
33765     findPanel : function(panelId){
33766         var rs = this.regions;
33767         for(var target in rs){
33768             if(typeof rs[target] != "function"){
33769                 var p = rs[target].getPanel(panelId);
33770                 if(p){
33771                     return p;
33772                 }
33773             }
33774         }
33775         return null;
33776     },
33777
33778     /**
33779      * Searches all regions for a panel with the specified id and activates (shows) it.
33780      * @param {String/ContentPanel} panelId The panels id or the panel itself
33781      * @return {Roo.ContentPanel} The shown panel or null
33782      */
33783     showPanel : function(panelId) {
33784       var rs = this.regions;
33785       for(var target in rs){
33786          var r = rs[target];
33787          if(typeof r != "function"){
33788             if(r.hasPanel(panelId)){
33789                return r.showPanel(panelId);
33790             }
33791          }
33792       }
33793       return null;
33794    },
33795
33796    /**
33797      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33798      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33799      */
33800     restoreState : function(provider){
33801         if(!provider){
33802             provider = Roo.state.Manager;
33803         }
33804         var sm = new Roo.LayoutStateManager();
33805         sm.init(this, provider);
33806     },
33807
33808     /**
33809      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33810      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33811      * a valid ContentPanel config object.  Example:
33812      * <pre><code>
33813 // Create the main layout
33814 var layout = new Roo.BorderLayout('main-ct', {
33815     west: {
33816         split:true,
33817         minSize: 175,
33818         titlebar: true
33819     },
33820     center: {
33821         title:'Components'
33822     }
33823 }, 'main-ct');
33824
33825 // Create and add multiple ContentPanels at once via configs
33826 layout.batchAdd({
33827    west: {
33828        id: 'source-files',
33829        autoCreate:true,
33830        title:'Ext Source Files',
33831        autoScroll:true,
33832        fitToFrame:true
33833    },
33834    center : {
33835        el: cview,
33836        autoScroll:true,
33837        fitToFrame:true,
33838        toolbar: tb,
33839        resizeEl:'cbody'
33840    }
33841 });
33842 </code></pre>
33843      * @param {Object} regions An object containing ContentPanel configs by region name
33844      */
33845     batchAdd : function(regions){
33846         this.beginUpdate();
33847         for(var rname in regions){
33848             var lr = this.regions[rname];
33849             if(lr){
33850                 this.addTypedPanels(lr, regions[rname]);
33851             }
33852         }
33853         this.endUpdate();
33854     },
33855
33856     // private
33857     addTypedPanels : function(lr, ps){
33858         if(typeof ps == 'string'){
33859             lr.add(new Roo.ContentPanel(ps));
33860         }
33861         else if(ps instanceof Array){
33862             for(var i =0, len = ps.length; i < len; i++){
33863                 this.addTypedPanels(lr, ps[i]);
33864             }
33865         }
33866         else if(!ps.events){ // raw config?
33867             var el = ps.el;
33868             delete ps.el; // prevent conflict
33869             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33870         }
33871         else {  // panel object assumed!
33872             lr.add(ps);
33873         }
33874     },
33875     /**
33876      * Adds a xtype elements to the layout.
33877      * <pre><code>
33878
33879 layout.addxtype({
33880        xtype : 'ContentPanel',
33881        region: 'west',
33882        items: [ .... ]
33883    }
33884 );
33885
33886 layout.addxtype({
33887         xtype : 'NestedLayoutPanel',
33888         region: 'west',
33889         layout: {
33890            center: { },
33891            west: { }   
33892         },
33893         items : [ ... list of content panels or nested layout panels.. ]
33894    }
33895 );
33896 </code></pre>
33897      * @param {Object} cfg Xtype definition of item to add.
33898      */
33899     addxtype : function(cfg)
33900     {
33901         // basically accepts a pannel...
33902         // can accept a layout region..!?!?
33903         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33904         
33905         if (!cfg.xtype.match(/Panel$/)) {
33906             return false;
33907         }
33908         var ret = false;
33909         
33910         if (typeof(cfg.region) == 'undefined') {
33911             Roo.log("Failed to add Panel, region was not set");
33912             Roo.log(cfg);
33913             return false;
33914         }
33915         var region = cfg.region;
33916         delete cfg.region;
33917         
33918           
33919         var xitems = [];
33920         if (cfg.items) {
33921             xitems = cfg.items;
33922             delete cfg.items;
33923         }
33924         var nb = false;
33925         
33926         switch(cfg.xtype) 
33927         {
33928             case 'ContentPanel':  // ContentPanel (el, cfg)
33929             case 'ScrollPanel':  // ContentPanel (el, cfg)
33930             case 'ViewPanel': 
33931                 if(cfg.autoCreate) {
33932                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33933                 } else {
33934                     var el = this.el.createChild();
33935                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33936                 }
33937                 
33938                 this.add(region, ret);
33939                 break;
33940             
33941             
33942             case 'TreePanel': // our new panel!
33943                 cfg.el = this.el.createChild();
33944                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33945                 this.add(region, ret);
33946                 break;
33947             
33948             case 'NestedLayoutPanel': 
33949                 // create a new Layout (which is  a Border Layout...
33950                 var el = this.el.createChild();
33951                 var clayout = cfg.layout;
33952                 delete cfg.layout;
33953                 clayout.items   = clayout.items  || [];
33954                 // replace this exitems with the clayout ones..
33955                 xitems = clayout.items;
33956                  
33957                 
33958                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33959                     cfg.background = false;
33960                 }
33961                 var layout = new Roo.BorderLayout(el, clayout);
33962                 
33963                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33964                 //console.log('adding nested layout panel '  + cfg.toSource());
33965                 this.add(region, ret);
33966                 nb = {}; /// find first...
33967                 break;
33968                 
33969             case 'GridPanel': 
33970             
33971                 // needs grid and region
33972                 
33973                 //var el = this.getRegion(region).el.createChild();
33974                 var el = this.el.createChild();
33975                 // create the grid first...
33976                 
33977                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33978                 delete cfg.grid;
33979                 if (region == 'center' && this.active ) {
33980                     cfg.background = false;
33981                 }
33982                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33983                 
33984                 this.add(region, ret);
33985                 if (cfg.background) {
33986                     ret.on('activate', function(gp) {
33987                         if (!gp.grid.rendered) {
33988                             gp.grid.render();
33989                         }
33990                     });
33991                 } else {
33992                     grid.render();
33993                 }
33994                 break;
33995            
33996            
33997            
33998                 
33999                 
34000                 
34001             default:
34002                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34003                     
34004                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34005                     this.add(region, ret);
34006                 } else {
34007                 
34008                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
34009                     return null;
34010                 }
34011                 
34012              // GridPanel (grid, cfg)
34013             
34014         }
34015         this.beginUpdate();
34016         // add children..
34017         var region = '';
34018         var abn = {};
34019         Roo.each(xitems, function(i)  {
34020             region = nb && i.region ? i.region : false;
34021             
34022             var add = ret.addxtype(i);
34023            
34024             if (region) {
34025                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34026                 if (!i.background) {
34027                     abn[region] = nb[region] ;
34028                 }
34029             }
34030             
34031         });
34032         this.endUpdate();
34033
34034         // make the last non-background panel active..
34035         //if (nb) { Roo.log(abn); }
34036         if (nb) {
34037             
34038             for(var r in abn) {
34039                 region = this.getRegion(r);
34040                 if (region) {
34041                     // tried using nb[r], but it does not work..
34042                      
34043                     region.showPanel(abn[r]);
34044                    
34045                 }
34046             }
34047         }
34048         return ret;
34049         
34050     }
34051 });
34052
34053 /**
34054  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34055  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
34056  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34057  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
34058  * <pre><code>
34059 // shorthand
34060 var CP = Roo.ContentPanel;
34061
34062 var layout = Roo.BorderLayout.create({
34063     north: {
34064         initialSize: 25,
34065         titlebar: false,
34066         panels: [new CP("north", "North")]
34067     },
34068     west: {
34069         split:true,
34070         initialSize: 200,
34071         minSize: 175,
34072         maxSize: 400,
34073         titlebar: true,
34074         collapsible: true,
34075         panels: [new CP("west", {title: "West"})]
34076     },
34077     east: {
34078         split:true,
34079         initialSize: 202,
34080         minSize: 175,
34081         maxSize: 400,
34082         titlebar: true,
34083         collapsible: true,
34084         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34085     },
34086     south: {
34087         split:true,
34088         initialSize: 100,
34089         minSize: 100,
34090         maxSize: 200,
34091         titlebar: true,
34092         collapsible: true,
34093         panels: [new CP("south", {title: "South", closable: true})]
34094     },
34095     center: {
34096         titlebar: true,
34097         autoScroll:true,
34098         resizeTabs: true,
34099         minTabWidth: 50,
34100         preferredTabWidth: 150,
34101         panels: [
34102             new CP("center1", {title: "Close Me", closable: true}),
34103             new CP("center2", {title: "Center Panel", closable: false})
34104         ]
34105     }
34106 }, document.body);
34107
34108 layout.getRegion("center").showPanel("center1");
34109 </code></pre>
34110  * @param config
34111  * @param targetEl
34112  */
34113 Roo.BorderLayout.create = function(config, targetEl){
34114     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34115     layout.beginUpdate();
34116     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34117     for(var j = 0, jlen = regions.length; j < jlen; j++){
34118         var lr = regions[j];
34119         if(layout.regions[lr] && config[lr].panels){
34120             var r = layout.regions[lr];
34121             var ps = config[lr].panels;
34122             layout.addTypedPanels(r, ps);
34123         }
34124     }
34125     layout.endUpdate();
34126     return layout;
34127 };
34128
34129 // private
34130 Roo.BorderLayout.RegionFactory = {
34131     // private
34132     validRegions : ["north","south","east","west","center"],
34133
34134     // private
34135     create : function(target, mgr, config){
34136         target = target.toLowerCase();
34137         if(config.lightweight || config.basic){
34138             return new Roo.BasicLayoutRegion(mgr, config, target);
34139         }
34140         switch(target){
34141             case "north":
34142                 return new Roo.NorthLayoutRegion(mgr, config);
34143             case "south":
34144                 return new Roo.SouthLayoutRegion(mgr, config);
34145             case "east":
34146                 return new Roo.EastLayoutRegion(mgr, config);
34147             case "west":
34148                 return new Roo.WestLayoutRegion(mgr, config);
34149             case "center":
34150                 return new Roo.CenterLayoutRegion(mgr, config);
34151         }
34152         throw 'Layout region "'+target+'" not supported.';
34153     }
34154 };/*
34155  * Based on:
34156  * Ext JS Library 1.1.1
34157  * Copyright(c) 2006-2007, Ext JS, LLC.
34158  *
34159  * Originally Released Under LGPL - original licence link has changed is not relivant.
34160  *
34161  * Fork - LGPL
34162  * <script type="text/javascript">
34163  */
34164  
34165 /**
34166  * @class Roo.BasicLayoutRegion
34167  * @extends Roo.util.Observable
34168  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34169  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34170  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34171  */
34172 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34173     this.mgr = mgr;
34174     this.position  = pos;
34175     this.events = {
34176         /**
34177          * @scope Roo.BasicLayoutRegion
34178          */
34179         
34180         /**
34181          * @event beforeremove
34182          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34183          * @param {Roo.LayoutRegion} this
34184          * @param {Roo.ContentPanel} panel The panel
34185          * @param {Object} e The cancel event object
34186          */
34187         "beforeremove" : true,
34188         /**
34189          * @event invalidated
34190          * Fires when the layout for this region is changed.
34191          * @param {Roo.LayoutRegion} this
34192          */
34193         "invalidated" : true,
34194         /**
34195          * @event visibilitychange
34196          * Fires when this region is shown or hidden 
34197          * @param {Roo.LayoutRegion} this
34198          * @param {Boolean} visibility true or false
34199          */
34200         "visibilitychange" : true,
34201         /**
34202          * @event paneladded
34203          * Fires when a panel is added. 
34204          * @param {Roo.LayoutRegion} this
34205          * @param {Roo.ContentPanel} panel The panel
34206          */
34207         "paneladded" : true,
34208         /**
34209          * @event panelremoved
34210          * Fires when a panel is removed. 
34211          * @param {Roo.LayoutRegion} this
34212          * @param {Roo.ContentPanel} panel The panel
34213          */
34214         "panelremoved" : true,
34215         /**
34216          * @event beforecollapse
34217          * Fires when this region before collapse.
34218          * @param {Roo.LayoutRegion} this
34219          */
34220         "beforecollapse" : true,
34221         /**
34222          * @event collapsed
34223          * Fires when this region is collapsed.
34224          * @param {Roo.LayoutRegion} this
34225          */
34226         "collapsed" : true,
34227         /**
34228          * @event expanded
34229          * Fires when this region is expanded.
34230          * @param {Roo.LayoutRegion} this
34231          */
34232         "expanded" : true,
34233         /**
34234          * @event slideshow
34235          * Fires when this region is slid into view.
34236          * @param {Roo.LayoutRegion} this
34237          */
34238         "slideshow" : true,
34239         /**
34240          * @event slidehide
34241          * Fires when this region slides out of view. 
34242          * @param {Roo.LayoutRegion} this
34243          */
34244         "slidehide" : true,
34245         /**
34246          * @event panelactivated
34247          * Fires when a panel is activated. 
34248          * @param {Roo.LayoutRegion} this
34249          * @param {Roo.ContentPanel} panel The activated panel
34250          */
34251         "panelactivated" : true,
34252         /**
34253          * @event resized
34254          * Fires when the user resizes this region. 
34255          * @param {Roo.LayoutRegion} this
34256          * @param {Number} newSize The new size (width for east/west, height for north/south)
34257          */
34258         "resized" : true
34259     };
34260     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34261     this.panels = new Roo.util.MixedCollection();
34262     this.panels.getKey = this.getPanelId.createDelegate(this);
34263     this.box = null;
34264     this.activePanel = null;
34265     // ensure listeners are added...
34266     
34267     if (config.listeners || config.events) {
34268         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34269             listeners : config.listeners || {},
34270             events : config.events || {}
34271         });
34272     }
34273     
34274     if(skipConfig !== true){
34275         this.applyConfig(config);
34276     }
34277 };
34278
34279 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34280     getPanelId : function(p){
34281         return p.getId();
34282     },
34283     
34284     applyConfig : function(config){
34285         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34286         this.config = config;
34287         
34288     },
34289     
34290     /**
34291      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34292      * the width, for horizontal (north, south) the height.
34293      * @param {Number} newSize The new width or height
34294      */
34295     resizeTo : function(newSize){
34296         var el = this.el ? this.el :
34297                  (this.activePanel ? this.activePanel.getEl() : null);
34298         if(el){
34299             switch(this.position){
34300                 case "east":
34301                 case "west":
34302                     el.setWidth(newSize);
34303                     this.fireEvent("resized", this, newSize);
34304                 break;
34305                 case "north":
34306                 case "south":
34307                     el.setHeight(newSize);
34308                     this.fireEvent("resized", this, newSize);
34309                 break;                
34310             }
34311         }
34312     },
34313     
34314     getBox : function(){
34315         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34316     },
34317     
34318     getMargins : function(){
34319         return this.margins;
34320     },
34321     
34322     updateBox : function(box){
34323         this.box = box;
34324         var el = this.activePanel.getEl();
34325         el.dom.style.left = box.x + "px";
34326         el.dom.style.top = box.y + "px";
34327         this.activePanel.setSize(box.width, box.height);
34328     },
34329     
34330     /**
34331      * Returns the container element for this region.
34332      * @return {Roo.Element}
34333      */
34334     getEl : function(){
34335         return this.activePanel;
34336     },
34337     
34338     /**
34339      * Returns true if this region is currently visible.
34340      * @return {Boolean}
34341      */
34342     isVisible : function(){
34343         return this.activePanel ? true : false;
34344     },
34345     
34346     setActivePanel : function(panel){
34347         panel = this.getPanel(panel);
34348         if(this.activePanel && this.activePanel != panel){
34349             this.activePanel.setActiveState(false);
34350             this.activePanel.getEl().setLeftTop(-10000,-10000);
34351         }
34352         this.activePanel = panel;
34353         panel.setActiveState(true);
34354         if(this.box){
34355             panel.setSize(this.box.width, this.box.height);
34356         }
34357         this.fireEvent("panelactivated", this, panel);
34358         this.fireEvent("invalidated");
34359     },
34360     
34361     /**
34362      * Show the specified panel.
34363      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34364      * @return {Roo.ContentPanel} The shown panel or null
34365      */
34366     showPanel : function(panel){
34367         if(panel = this.getPanel(panel)){
34368             this.setActivePanel(panel);
34369         }
34370         return panel;
34371     },
34372     
34373     /**
34374      * Get the active panel for this region.
34375      * @return {Roo.ContentPanel} The active panel or null
34376      */
34377     getActivePanel : function(){
34378         return this.activePanel;
34379     },
34380     
34381     /**
34382      * Add the passed ContentPanel(s)
34383      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34384      * @return {Roo.ContentPanel} The panel added (if only one was added)
34385      */
34386     add : function(panel){
34387         if(arguments.length > 1){
34388             for(var i = 0, len = arguments.length; i < len; i++) {
34389                 this.add(arguments[i]);
34390             }
34391             return null;
34392         }
34393         if(this.hasPanel(panel)){
34394             this.showPanel(panel);
34395             return panel;
34396         }
34397         var el = panel.getEl();
34398         if(el.dom.parentNode != this.mgr.el.dom){
34399             this.mgr.el.dom.appendChild(el.dom);
34400         }
34401         if(panel.setRegion){
34402             panel.setRegion(this);
34403         }
34404         this.panels.add(panel);
34405         el.setStyle("position", "absolute");
34406         if(!panel.background){
34407             this.setActivePanel(panel);
34408             if(this.config.initialSize && this.panels.getCount()==1){
34409                 this.resizeTo(this.config.initialSize);
34410             }
34411         }
34412         this.fireEvent("paneladded", this, panel);
34413         return panel;
34414     },
34415     
34416     /**
34417      * Returns true if the panel is in this region.
34418      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34419      * @return {Boolean}
34420      */
34421     hasPanel : function(panel){
34422         if(typeof panel == "object"){ // must be panel obj
34423             panel = panel.getId();
34424         }
34425         return this.getPanel(panel) ? true : false;
34426     },
34427     
34428     /**
34429      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34430      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34431      * @param {Boolean} preservePanel Overrides the config preservePanel option
34432      * @return {Roo.ContentPanel} The panel that was removed
34433      */
34434     remove : function(panel, preservePanel){
34435         panel = this.getPanel(panel);
34436         if(!panel){
34437             return null;
34438         }
34439         var e = {};
34440         this.fireEvent("beforeremove", this, panel, e);
34441         if(e.cancel === true){
34442             return null;
34443         }
34444         var panelId = panel.getId();
34445         this.panels.removeKey(panelId);
34446         return panel;
34447     },
34448     
34449     /**
34450      * Returns the panel specified or null if it's not in this region.
34451      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34452      * @return {Roo.ContentPanel}
34453      */
34454     getPanel : function(id){
34455         if(typeof id == "object"){ // must be panel obj
34456             return id;
34457         }
34458         return this.panels.get(id);
34459     },
34460     
34461     /**
34462      * Returns this regions position (north/south/east/west/center).
34463      * @return {String} 
34464      */
34465     getPosition: function(){
34466         return this.position;    
34467     }
34468 });/*
34469  * Based on:
34470  * Ext JS Library 1.1.1
34471  * Copyright(c) 2006-2007, Ext JS, LLC.
34472  *
34473  * Originally Released Under LGPL - original licence link has changed is not relivant.
34474  *
34475  * Fork - LGPL
34476  * <script type="text/javascript">
34477  */
34478  
34479 /**
34480  * @class Roo.LayoutRegion
34481  * @extends Roo.BasicLayoutRegion
34482  * This class represents a region in a layout manager.
34483  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
34484  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
34485  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
34486  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34487  * @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})
34488  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34489  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
34490  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34491  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34492  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34493  * @cfg {String}    title           The title for the region (overrides panel titles)
34494  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34495  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34496  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34497  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34498  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34499  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34500  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34501  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34502  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34503  * @cfg {Boolean}   showPin         True to show a pin button
34504  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34505  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34506  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34507  * @cfg {Number}    width           For East/West panels
34508  * @cfg {Number}    height          For North/South panels
34509  * @cfg {Boolean}   split           To show the splitter
34510  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34511  */
34512 Roo.LayoutRegion = function(mgr, config, pos){
34513     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34514     var dh = Roo.DomHelper;
34515     /** This region's container element 
34516     * @type Roo.Element */
34517     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34518     /** This region's title element 
34519     * @type Roo.Element */
34520
34521     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34522         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34523         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34524     ]}, true);
34525     this.titleEl.enableDisplayMode();
34526     /** This region's title text element 
34527     * @type HTMLElement */
34528     this.titleTextEl = this.titleEl.dom.firstChild;
34529     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34530     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34531     this.closeBtn.enableDisplayMode();
34532     this.closeBtn.on("click", this.closeClicked, this);
34533     this.closeBtn.hide();
34534
34535     this.createBody(config);
34536     this.visible = true;
34537     this.collapsed = false;
34538
34539     if(config.hideWhenEmpty){
34540         this.hide();
34541         this.on("paneladded", this.validateVisibility, this);
34542         this.on("panelremoved", this.validateVisibility, this);
34543     }
34544     this.applyConfig(config);
34545 };
34546
34547 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34548
34549     createBody : function(){
34550         /** This region's body element 
34551         * @type Roo.Element */
34552         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34553     },
34554
34555     applyConfig : function(c){
34556         if(c.collapsible && this.position != "center" && !this.collapsedEl){
34557             var dh = Roo.DomHelper;
34558             if(c.titlebar !== false){
34559                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34560                 this.collapseBtn.on("click", this.collapse, this);
34561                 this.collapseBtn.enableDisplayMode();
34562
34563                 if(c.showPin === true || this.showPin){
34564                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34565                     this.stickBtn.enableDisplayMode();
34566                     this.stickBtn.on("click", this.expand, this);
34567                     this.stickBtn.hide();
34568                 }
34569             }
34570             /** This region's collapsed element
34571             * @type Roo.Element */
34572             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34573                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34574             ]}, true);
34575             if(c.floatable !== false){
34576                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34577                this.collapsedEl.on("click", this.collapseClick, this);
34578             }
34579
34580             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34581                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34582                    id: "message", unselectable: "on", style:{"float":"left"}});
34583                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34584              }
34585             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34586             this.expandBtn.on("click", this.expand, this);
34587         }
34588         if(this.collapseBtn){
34589             this.collapseBtn.setVisible(c.collapsible == true);
34590         }
34591         this.cmargins = c.cmargins || this.cmargins ||
34592                          (this.position == "west" || this.position == "east" ?
34593                              {top: 0, left: 2, right:2, bottom: 0} :
34594                              {top: 2, left: 0, right:0, bottom: 2});
34595         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34596         this.bottomTabs = c.tabPosition != "top";
34597         this.autoScroll = c.autoScroll || false;
34598         if(this.autoScroll){
34599             this.bodyEl.setStyle("overflow", "auto");
34600         }else{
34601             this.bodyEl.setStyle("overflow", "hidden");
34602         }
34603         //if(c.titlebar !== false){
34604             if((!c.titlebar && !c.title) || c.titlebar === false){
34605                 this.titleEl.hide();
34606             }else{
34607                 this.titleEl.show();
34608                 if(c.title){
34609                     this.titleTextEl.innerHTML = c.title;
34610                 }
34611             }
34612         //}
34613         this.duration = c.duration || .30;
34614         this.slideDuration = c.slideDuration || .45;
34615         this.config = c;
34616         if(c.collapsed){
34617             this.collapse(true);
34618         }
34619         if(c.hidden){
34620             this.hide();
34621         }
34622     },
34623     /**
34624      * Returns true if this region is currently visible.
34625      * @return {Boolean}
34626      */
34627     isVisible : function(){
34628         return this.visible;
34629     },
34630
34631     /**
34632      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34633      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34634      */
34635     setCollapsedTitle : function(title){
34636         title = title || "&#160;";
34637         if(this.collapsedTitleTextEl){
34638             this.collapsedTitleTextEl.innerHTML = title;
34639         }
34640     },
34641
34642     getBox : function(){
34643         var b;
34644         if(!this.collapsed){
34645             b = this.el.getBox(false, true);
34646         }else{
34647             b = this.collapsedEl.getBox(false, true);
34648         }
34649         return b;
34650     },
34651
34652     getMargins : function(){
34653         return this.collapsed ? this.cmargins : this.margins;
34654     },
34655
34656     highlight : function(){
34657         this.el.addClass("x-layout-panel-dragover");
34658     },
34659
34660     unhighlight : function(){
34661         this.el.removeClass("x-layout-panel-dragover");
34662     },
34663
34664     updateBox : function(box){
34665         this.box = box;
34666         if(!this.collapsed){
34667             this.el.dom.style.left = box.x + "px";
34668             this.el.dom.style.top = box.y + "px";
34669             this.updateBody(box.width, box.height);
34670         }else{
34671             this.collapsedEl.dom.style.left = box.x + "px";
34672             this.collapsedEl.dom.style.top = box.y + "px";
34673             this.collapsedEl.setSize(box.width, box.height);
34674         }
34675         if(this.tabs){
34676             this.tabs.autoSizeTabs();
34677         }
34678     },
34679
34680     updateBody : function(w, h){
34681         if(w !== null){
34682             this.el.setWidth(w);
34683             w -= this.el.getBorderWidth("rl");
34684             if(this.config.adjustments){
34685                 w += this.config.adjustments[0];
34686             }
34687         }
34688         if(h !== null){
34689             this.el.setHeight(h);
34690             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34691             h -= this.el.getBorderWidth("tb");
34692             if(this.config.adjustments){
34693                 h += this.config.adjustments[1];
34694             }
34695             this.bodyEl.setHeight(h);
34696             if(this.tabs){
34697                 h = this.tabs.syncHeight(h);
34698             }
34699         }
34700         if(this.panelSize){
34701             w = w !== null ? w : this.panelSize.width;
34702             h = h !== null ? h : this.panelSize.height;
34703         }
34704         if(this.activePanel){
34705             var el = this.activePanel.getEl();
34706             w = w !== null ? w : el.getWidth();
34707             h = h !== null ? h : el.getHeight();
34708             this.panelSize = {width: w, height: h};
34709             this.activePanel.setSize(w, h);
34710         }
34711         if(Roo.isIE && this.tabs){
34712             this.tabs.el.repaint();
34713         }
34714     },
34715
34716     /**
34717      * Returns the container element for this region.
34718      * @return {Roo.Element}
34719      */
34720     getEl : function(){
34721         return this.el;
34722     },
34723
34724     /**
34725      * Hides this region.
34726      */
34727     hide : function(){
34728         if(!this.collapsed){
34729             this.el.dom.style.left = "-2000px";
34730             this.el.hide();
34731         }else{
34732             this.collapsedEl.dom.style.left = "-2000px";
34733             this.collapsedEl.hide();
34734         }
34735         this.visible = false;
34736         this.fireEvent("visibilitychange", this, false);
34737     },
34738
34739     /**
34740      * Shows this region if it was previously hidden.
34741      */
34742     show : function(){
34743         if(!this.collapsed){
34744             this.el.show();
34745         }else{
34746             this.collapsedEl.show();
34747         }
34748         this.visible = true;
34749         this.fireEvent("visibilitychange", this, true);
34750     },
34751
34752     closeClicked : function(){
34753         if(this.activePanel){
34754             this.remove(this.activePanel);
34755         }
34756     },
34757
34758     collapseClick : function(e){
34759         if(this.isSlid){
34760            e.stopPropagation();
34761            this.slideIn();
34762         }else{
34763            e.stopPropagation();
34764            this.slideOut();
34765         }
34766     },
34767
34768     /**
34769      * Collapses this region.
34770      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34771      */
34772     collapse : function(skipAnim, skipCheck){
34773         if(this.collapsed) {
34774             return;
34775         }
34776         
34777         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34778             
34779             this.collapsed = true;
34780             if(this.split){
34781                 this.split.el.hide();
34782             }
34783             if(this.config.animate && skipAnim !== true){
34784                 this.fireEvent("invalidated", this);
34785                 this.animateCollapse();
34786             }else{
34787                 this.el.setLocation(-20000,-20000);
34788                 this.el.hide();
34789                 this.collapsedEl.show();
34790                 this.fireEvent("collapsed", this);
34791                 this.fireEvent("invalidated", this);
34792             }
34793         }
34794         
34795     },
34796
34797     animateCollapse : function(){
34798         // overridden
34799     },
34800
34801     /**
34802      * Expands this region if it was previously collapsed.
34803      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34804      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34805      */
34806     expand : function(e, skipAnim){
34807         if(e) {
34808             e.stopPropagation();
34809         }
34810         if(!this.collapsed || this.el.hasActiveFx()) {
34811             return;
34812         }
34813         if(this.isSlid){
34814             this.afterSlideIn();
34815             skipAnim = true;
34816         }
34817         this.collapsed = false;
34818         if(this.config.animate && skipAnim !== true){
34819             this.animateExpand();
34820         }else{
34821             this.el.show();
34822             if(this.split){
34823                 this.split.el.show();
34824             }
34825             this.collapsedEl.setLocation(-2000,-2000);
34826             this.collapsedEl.hide();
34827             this.fireEvent("invalidated", this);
34828             this.fireEvent("expanded", this);
34829         }
34830     },
34831
34832     animateExpand : function(){
34833         // overridden
34834     },
34835
34836     initTabs : function()
34837     {
34838         this.bodyEl.setStyle("overflow", "hidden");
34839         var ts = new Roo.TabPanel(
34840                 this.bodyEl.dom,
34841                 {
34842                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34843                     disableTooltips: this.config.disableTabTips,
34844                     toolbar : this.config.toolbar
34845                 }
34846         );
34847         if(this.config.hideTabs){
34848             ts.stripWrap.setDisplayed(false);
34849         }
34850         this.tabs = ts;
34851         ts.resizeTabs = this.config.resizeTabs === true;
34852         ts.minTabWidth = this.config.minTabWidth || 40;
34853         ts.maxTabWidth = this.config.maxTabWidth || 250;
34854         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34855         ts.monitorResize = false;
34856         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34857         ts.bodyEl.addClass('x-layout-tabs-body');
34858         this.panels.each(this.initPanelAsTab, this);
34859     },
34860
34861     initPanelAsTab : function(panel){
34862         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34863                     this.config.closeOnTab && panel.isClosable());
34864         if(panel.tabTip !== undefined){
34865             ti.setTooltip(panel.tabTip);
34866         }
34867         ti.on("activate", function(){
34868               this.setActivePanel(panel);
34869         }, this);
34870         if(this.config.closeOnTab){
34871             ti.on("beforeclose", function(t, e){
34872                 e.cancel = true;
34873                 this.remove(panel);
34874             }, this);
34875         }
34876         return ti;
34877     },
34878
34879     updatePanelTitle : function(panel, title){
34880         if(this.activePanel == panel){
34881             this.updateTitle(title);
34882         }
34883         if(this.tabs){
34884             var ti = this.tabs.getTab(panel.getEl().id);
34885             ti.setText(title);
34886             if(panel.tabTip !== undefined){
34887                 ti.setTooltip(panel.tabTip);
34888             }
34889         }
34890     },
34891
34892     updateTitle : function(title){
34893         if(this.titleTextEl && !this.config.title){
34894             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34895         }
34896     },
34897
34898     setActivePanel : function(panel){
34899         panel = this.getPanel(panel);
34900         if(this.activePanel && this.activePanel != panel){
34901             this.activePanel.setActiveState(false);
34902         }
34903         this.activePanel = panel;
34904         panel.setActiveState(true);
34905         if(this.panelSize){
34906             panel.setSize(this.panelSize.width, this.panelSize.height);
34907         }
34908         if(this.closeBtn){
34909             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34910         }
34911         this.updateTitle(panel.getTitle());
34912         if(this.tabs){
34913             this.fireEvent("invalidated", this);
34914         }
34915         this.fireEvent("panelactivated", this, panel);
34916     },
34917
34918     /**
34919      * Shows the specified panel.
34920      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34921      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34922      */
34923     showPanel : function(panel)
34924     {
34925         panel = this.getPanel(panel);
34926         if(panel){
34927             if(this.tabs){
34928                 var tab = this.tabs.getTab(panel.getEl().id);
34929                 if(tab.isHidden()){
34930                     this.tabs.unhideTab(tab.id);
34931                 }
34932                 tab.activate();
34933             }else{
34934                 this.setActivePanel(panel);
34935             }
34936         }
34937         return panel;
34938     },
34939
34940     /**
34941      * Get the active panel for this region.
34942      * @return {Roo.ContentPanel} The active panel or null
34943      */
34944     getActivePanel : function(){
34945         return this.activePanel;
34946     },
34947
34948     validateVisibility : function(){
34949         if(this.panels.getCount() < 1){
34950             this.updateTitle("&#160;");
34951             this.closeBtn.hide();
34952             this.hide();
34953         }else{
34954             if(!this.isVisible()){
34955                 this.show();
34956             }
34957         }
34958     },
34959
34960     /**
34961      * Adds the passed ContentPanel(s) to this region.
34962      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34963      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34964      */
34965     add : function(panel){
34966         if(arguments.length > 1){
34967             for(var i = 0, len = arguments.length; i < len; i++) {
34968                 this.add(arguments[i]);
34969             }
34970             return null;
34971         }
34972         if(this.hasPanel(panel)){
34973             this.showPanel(panel);
34974             return panel;
34975         }
34976         panel.setRegion(this);
34977         this.panels.add(panel);
34978         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34979             this.bodyEl.dom.appendChild(panel.getEl().dom);
34980             if(panel.background !== true){
34981                 this.setActivePanel(panel);
34982             }
34983             this.fireEvent("paneladded", this, panel);
34984             return panel;
34985         }
34986         if(!this.tabs){
34987             this.initTabs();
34988         }else{
34989             this.initPanelAsTab(panel);
34990         }
34991         if(panel.background !== true){
34992             this.tabs.activate(panel.getEl().id);
34993         }
34994         this.fireEvent("paneladded", this, panel);
34995         return panel;
34996     },
34997
34998     /**
34999      * Hides the tab for the specified panel.
35000      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35001      */
35002     hidePanel : function(panel){
35003         if(this.tabs && (panel = this.getPanel(panel))){
35004             this.tabs.hideTab(panel.getEl().id);
35005         }
35006     },
35007
35008     /**
35009      * Unhides the tab for a previously hidden panel.
35010      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35011      */
35012     unhidePanel : function(panel){
35013         if(this.tabs && (panel = this.getPanel(panel))){
35014             this.tabs.unhideTab(panel.getEl().id);
35015         }
35016     },
35017
35018     clearPanels : function(){
35019         while(this.panels.getCount() > 0){
35020              this.remove(this.panels.first());
35021         }
35022     },
35023
35024     /**
35025      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35026      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35027      * @param {Boolean} preservePanel Overrides the config preservePanel option
35028      * @return {Roo.ContentPanel} The panel that was removed
35029      */
35030     remove : function(panel, preservePanel){
35031         panel = this.getPanel(panel);
35032         if(!panel){
35033             return null;
35034         }
35035         var e = {};
35036         this.fireEvent("beforeremove", this, panel, e);
35037         if(e.cancel === true){
35038             return null;
35039         }
35040         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35041         var panelId = panel.getId();
35042         this.panels.removeKey(panelId);
35043         if(preservePanel){
35044             document.body.appendChild(panel.getEl().dom);
35045         }
35046         if(this.tabs){
35047             this.tabs.removeTab(panel.getEl().id);
35048         }else if (!preservePanel){
35049             this.bodyEl.dom.removeChild(panel.getEl().dom);
35050         }
35051         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35052             var p = this.panels.first();
35053             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35054             tempEl.appendChild(p.getEl().dom);
35055             this.bodyEl.update("");
35056             this.bodyEl.dom.appendChild(p.getEl().dom);
35057             tempEl = null;
35058             this.updateTitle(p.getTitle());
35059             this.tabs = null;
35060             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35061             this.setActivePanel(p);
35062         }
35063         panel.setRegion(null);
35064         if(this.activePanel == panel){
35065             this.activePanel = null;
35066         }
35067         if(this.config.autoDestroy !== false && preservePanel !== true){
35068             try{panel.destroy();}catch(e){}
35069         }
35070         this.fireEvent("panelremoved", this, panel);
35071         return panel;
35072     },
35073
35074     /**
35075      * Returns the TabPanel component used by this region
35076      * @return {Roo.TabPanel}
35077      */
35078     getTabs : function(){
35079         return this.tabs;
35080     },
35081
35082     createTool : function(parentEl, className){
35083         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35084             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35085         btn.addClassOnOver("x-layout-tools-button-over");
35086         return btn;
35087     }
35088 });/*
35089  * Based on:
35090  * Ext JS Library 1.1.1
35091  * Copyright(c) 2006-2007, Ext JS, LLC.
35092  *
35093  * Originally Released Under LGPL - original licence link has changed is not relivant.
35094  *
35095  * Fork - LGPL
35096  * <script type="text/javascript">
35097  */
35098  
35099
35100
35101 /**
35102  * @class Roo.SplitLayoutRegion
35103  * @extends Roo.LayoutRegion
35104  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35105  */
35106 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35107     this.cursor = cursor;
35108     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35109 };
35110
35111 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35112     splitTip : "Drag to resize.",
35113     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35114     useSplitTips : false,
35115
35116     applyConfig : function(config){
35117         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35118         if(config.split){
35119             if(!this.split){
35120                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35121                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35122                 /** The SplitBar for this region 
35123                 * @type Roo.SplitBar */
35124                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35125                 this.split.on("moved", this.onSplitMove, this);
35126                 this.split.useShim = config.useShim === true;
35127                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35128                 if(this.useSplitTips){
35129                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35130                 }
35131                 if(config.collapsible){
35132                     this.split.el.on("dblclick", this.collapse,  this);
35133                 }
35134             }
35135             if(typeof config.minSize != "undefined"){
35136                 this.split.minSize = config.minSize;
35137             }
35138             if(typeof config.maxSize != "undefined"){
35139                 this.split.maxSize = config.maxSize;
35140             }
35141             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35142                 this.hideSplitter();
35143             }
35144         }
35145     },
35146
35147     getHMaxSize : function(){
35148          var cmax = this.config.maxSize || 10000;
35149          var center = this.mgr.getRegion("center");
35150          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35151     },
35152
35153     getVMaxSize : function(){
35154          var cmax = this.config.maxSize || 10000;
35155          var center = this.mgr.getRegion("center");
35156          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35157     },
35158
35159     onSplitMove : function(split, newSize){
35160         this.fireEvent("resized", this, newSize);
35161     },
35162     
35163     /** 
35164      * Returns the {@link Roo.SplitBar} for this region.
35165      * @return {Roo.SplitBar}
35166      */
35167     getSplitBar : function(){
35168         return this.split;
35169     },
35170     
35171     hide : function(){
35172         this.hideSplitter();
35173         Roo.SplitLayoutRegion.superclass.hide.call(this);
35174     },
35175
35176     hideSplitter : function(){
35177         if(this.split){
35178             this.split.el.setLocation(-2000,-2000);
35179             this.split.el.hide();
35180         }
35181     },
35182
35183     show : function(){
35184         if(this.split){
35185             this.split.el.show();
35186         }
35187         Roo.SplitLayoutRegion.superclass.show.call(this);
35188     },
35189     
35190     beforeSlide: function(){
35191         if(Roo.isGecko){// firefox overflow auto bug workaround
35192             this.bodyEl.clip();
35193             if(this.tabs) {
35194                 this.tabs.bodyEl.clip();
35195             }
35196             if(this.activePanel){
35197                 this.activePanel.getEl().clip();
35198                 
35199                 if(this.activePanel.beforeSlide){
35200                     this.activePanel.beforeSlide();
35201                 }
35202             }
35203         }
35204     },
35205     
35206     afterSlide : function(){
35207         if(Roo.isGecko){// firefox overflow auto bug workaround
35208             this.bodyEl.unclip();
35209             if(this.tabs) {
35210                 this.tabs.bodyEl.unclip();
35211             }
35212             if(this.activePanel){
35213                 this.activePanel.getEl().unclip();
35214                 if(this.activePanel.afterSlide){
35215                     this.activePanel.afterSlide();
35216                 }
35217             }
35218         }
35219     },
35220
35221     initAutoHide : function(){
35222         if(this.autoHide !== false){
35223             if(!this.autoHideHd){
35224                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35225                 this.autoHideHd = {
35226                     "mouseout": function(e){
35227                         if(!e.within(this.el, true)){
35228                             st.delay(500);
35229                         }
35230                     },
35231                     "mouseover" : function(e){
35232                         st.cancel();
35233                     },
35234                     scope : this
35235                 };
35236             }
35237             this.el.on(this.autoHideHd);
35238         }
35239     },
35240
35241     clearAutoHide : function(){
35242         if(this.autoHide !== false){
35243             this.el.un("mouseout", this.autoHideHd.mouseout);
35244             this.el.un("mouseover", this.autoHideHd.mouseover);
35245         }
35246     },
35247
35248     clearMonitor : function(){
35249         Roo.get(document).un("click", this.slideInIf, this);
35250     },
35251
35252     // these names are backwards but not changed for compat
35253     slideOut : function(){
35254         if(this.isSlid || this.el.hasActiveFx()){
35255             return;
35256         }
35257         this.isSlid = true;
35258         if(this.collapseBtn){
35259             this.collapseBtn.hide();
35260         }
35261         this.closeBtnState = this.closeBtn.getStyle('display');
35262         this.closeBtn.hide();
35263         if(this.stickBtn){
35264             this.stickBtn.show();
35265         }
35266         this.el.show();
35267         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35268         this.beforeSlide();
35269         this.el.setStyle("z-index", 10001);
35270         this.el.slideIn(this.getSlideAnchor(), {
35271             callback: function(){
35272                 this.afterSlide();
35273                 this.initAutoHide();
35274                 Roo.get(document).on("click", this.slideInIf, this);
35275                 this.fireEvent("slideshow", this);
35276             },
35277             scope: this,
35278             block: true
35279         });
35280     },
35281
35282     afterSlideIn : function(){
35283         this.clearAutoHide();
35284         this.isSlid = false;
35285         this.clearMonitor();
35286         this.el.setStyle("z-index", "");
35287         if(this.collapseBtn){
35288             this.collapseBtn.show();
35289         }
35290         this.closeBtn.setStyle('display', this.closeBtnState);
35291         if(this.stickBtn){
35292             this.stickBtn.hide();
35293         }
35294         this.fireEvent("slidehide", this);
35295     },
35296
35297     slideIn : function(cb){
35298         if(!this.isSlid || this.el.hasActiveFx()){
35299             Roo.callback(cb);
35300             return;
35301         }
35302         this.isSlid = false;
35303         this.beforeSlide();
35304         this.el.slideOut(this.getSlideAnchor(), {
35305             callback: function(){
35306                 this.el.setLeftTop(-10000, -10000);
35307                 this.afterSlide();
35308                 this.afterSlideIn();
35309                 Roo.callback(cb);
35310             },
35311             scope: this,
35312             block: true
35313         });
35314     },
35315     
35316     slideInIf : function(e){
35317         if(!e.within(this.el)){
35318             this.slideIn();
35319         }
35320     },
35321
35322     animateCollapse : function(){
35323         this.beforeSlide();
35324         this.el.setStyle("z-index", 20000);
35325         var anchor = this.getSlideAnchor();
35326         this.el.slideOut(anchor, {
35327             callback : function(){
35328                 this.el.setStyle("z-index", "");
35329                 this.collapsedEl.slideIn(anchor, {duration:.3});
35330                 this.afterSlide();
35331                 this.el.setLocation(-10000,-10000);
35332                 this.el.hide();
35333                 this.fireEvent("collapsed", this);
35334             },
35335             scope: this,
35336             block: true
35337         });
35338     },
35339
35340     animateExpand : function(){
35341         this.beforeSlide();
35342         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35343         this.el.setStyle("z-index", 20000);
35344         this.collapsedEl.hide({
35345             duration:.1
35346         });
35347         this.el.slideIn(this.getSlideAnchor(), {
35348             callback : function(){
35349                 this.el.setStyle("z-index", "");
35350                 this.afterSlide();
35351                 if(this.split){
35352                     this.split.el.show();
35353                 }
35354                 this.fireEvent("invalidated", this);
35355                 this.fireEvent("expanded", this);
35356             },
35357             scope: this,
35358             block: true
35359         });
35360     },
35361
35362     anchors : {
35363         "west" : "left",
35364         "east" : "right",
35365         "north" : "top",
35366         "south" : "bottom"
35367     },
35368
35369     sanchors : {
35370         "west" : "l",
35371         "east" : "r",
35372         "north" : "t",
35373         "south" : "b"
35374     },
35375
35376     canchors : {
35377         "west" : "tl-tr",
35378         "east" : "tr-tl",
35379         "north" : "tl-bl",
35380         "south" : "bl-tl"
35381     },
35382
35383     getAnchor : function(){
35384         return this.anchors[this.position];
35385     },
35386
35387     getCollapseAnchor : function(){
35388         return this.canchors[this.position];
35389     },
35390
35391     getSlideAnchor : function(){
35392         return this.sanchors[this.position];
35393     },
35394
35395     getAlignAdj : function(){
35396         var cm = this.cmargins;
35397         switch(this.position){
35398             case "west":
35399                 return [0, 0];
35400             break;
35401             case "east":
35402                 return [0, 0];
35403             break;
35404             case "north":
35405                 return [0, 0];
35406             break;
35407             case "south":
35408                 return [0, 0];
35409             break;
35410         }
35411     },
35412
35413     getExpandAdj : function(){
35414         var c = this.collapsedEl, cm = this.cmargins;
35415         switch(this.position){
35416             case "west":
35417                 return [-(cm.right+c.getWidth()+cm.left), 0];
35418             break;
35419             case "east":
35420                 return [cm.right+c.getWidth()+cm.left, 0];
35421             break;
35422             case "north":
35423                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35424             break;
35425             case "south":
35426                 return [0, cm.top+cm.bottom+c.getHeight()];
35427             break;
35428         }
35429     }
35430 });/*
35431  * Based on:
35432  * Ext JS Library 1.1.1
35433  * Copyright(c) 2006-2007, Ext JS, LLC.
35434  *
35435  * Originally Released Under LGPL - original licence link has changed is not relivant.
35436  *
35437  * Fork - LGPL
35438  * <script type="text/javascript">
35439  */
35440 /*
35441  * These classes are private internal classes
35442  */
35443 Roo.CenterLayoutRegion = function(mgr, config){
35444     Roo.LayoutRegion.call(this, mgr, config, "center");
35445     this.visible = true;
35446     this.minWidth = config.minWidth || 20;
35447     this.minHeight = config.minHeight || 20;
35448 };
35449
35450 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35451     hide : function(){
35452         // center panel can't be hidden
35453     },
35454     
35455     show : function(){
35456         // center panel can't be hidden
35457     },
35458     
35459     getMinWidth: function(){
35460         return this.minWidth;
35461     },
35462     
35463     getMinHeight: function(){
35464         return this.minHeight;
35465     }
35466 });
35467
35468
35469 Roo.NorthLayoutRegion = function(mgr, config){
35470     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35471     if(this.split){
35472         this.split.placement = Roo.SplitBar.TOP;
35473         this.split.orientation = Roo.SplitBar.VERTICAL;
35474         this.split.el.addClass("x-layout-split-v");
35475     }
35476     var size = config.initialSize || config.height;
35477     if(typeof size != "undefined"){
35478         this.el.setHeight(size);
35479     }
35480 };
35481 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35482     orientation: Roo.SplitBar.VERTICAL,
35483     getBox : function(){
35484         if(this.collapsed){
35485             return this.collapsedEl.getBox();
35486         }
35487         var box = this.el.getBox();
35488         if(this.split){
35489             box.height += this.split.el.getHeight();
35490         }
35491         return box;
35492     },
35493     
35494     updateBox : function(box){
35495         if(this.split && !this.collapsed){
35496             box.height -= this.split.el.getHeight();
35497             this.split.el.setLeft(box.x);
35498             this.split.el.setTop(box.y+box.height);
35499             this.split.el.setWidth(box.width);
35500         }
35501         if(this.collapsed){
35502             this.updateBody(box.width, null);
35503         }
35504         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35505     }
35506 });
35507
35508 Roo.SouthLayoutRegion = function(mgr, config){
35509     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35510     if(this.split){
35511         this.split.placement = Roo.SplitBar.BOTTOM;
35512         this.split.orientation = Roo.SplitBar.VERTICAL;
35513         this.split.el.addClass("x-layout-split-v");
35514     }
35515     var size = config.initialSize || config.height;
35516     if(typeof size != "undefined"){
35517         this.el.setHeight(size);
35518     }
35519 };
35520 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35521     orientation: Roo.SplitBar.VERTICAL,
35522     getBox : function(){
35523         if(this.collapsed){
35524             return this.collapsedEl.getBox();
35525         }
35526         var box = this.el.getBox();
35527         if(this.split){
35528             var sh = this.split.el.getHeight();
35529             box.height += sh;
35530             box.y -= sh;
35531         }
35532         return box;
35533     },
35534     
35535     updateBox : function(box){
35536         if(this.split && !this.collapsed){
35537             var sh = this.split.el.getHeight();
35538             box.height -= sh;
35539             box.y += sh;
35540             this.split.el.setLeft(box.x);
35541             this.split.el.setTop(box.y-sh);
35542             this.split.el.setWidth(box.width);
35543         }
35544         if(this.collapsed){
35545             this.updateBody(box.width, null);
35546         }
35547         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35548     }
35549 });
35550
35551 Roo.EastLayoutRegion = function(mgr, config){
35552     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35553     if(this.split){
35554         this.split.placement = Roo.SplitBar.RIGHT;
35555         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35556         this.split.el.addClass("x-layout-split-h");
35557     }
35558     var size = config.initialSize || config.width;
35559     if(typeof size != "undefined"){
35560         this.el.setWidth(size);
35561     }
35562 };
35563 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35564     orientation: Roo.SplitBar.HORIZONTAL,
35565     getBox : function(){
35566         if(this.collapsed){
35567             return this.collapsedEl.getBox();
35568         }
35569         var box = this.el.getBox();
35570         if(this.split){
35571             var sw = this.split.el.getWidth();
35572             box.width += sw;
35573             box.x -= sw;
35574         }
35575         return box;
35576     },
35577
35578     updateBox : function(box){
35579         if(this.split && !this.collapsed){
35580             var sw = this.split.el.getWidth();
35581             box.width -= sw;
35582             this.split.el.setLeft(box.x);
35583             this.split.el.setTop(box.y);
35584             this.split.el.setHeight(box.height);
35585             box.x += sw;
35586         }
35587         if(this.collapsed){
35588             this.updateBody(null, box.height);
35589         }
35590         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35591     }
35592 });
35593
35594 Roo.WestLayoutRegion = function(mgr, config){
35595     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35596     if(this.split){
35597         this.split.placement = Roo.SplitBar.LEFT;
35598         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35599         this.split.el.addClass("x-layout-split-h");
35600     }
35601     var size = config.initialSize || config.width;
35602     if(typeof size != "undefined"){
35603         this.el.setWidth(size);
35604     }
35605 };
35606 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35607     orientation: Roo.SplitBar.HORIZONTAL,
35608     getBox : function(){
35609         if(this.collapsed){
35610             return this.collapsedEl.getBox();
35611         }
35612         var box = this.el.getBox();
35613         if(this.split){
35614             box.width += this.split.el.getWidth();
35615         }
35616         return box;
35617     },
35618     
35619     updateBox : function(box){
35620         if(this.split && !this.collapsed){
35621             var sw = this.split.el.getWidth();
35622             box.width -= sw;
35623             this.split.el.setLeft(box.x+box.width);
35624             this.split.el.setTop(box.y);
35625             this.split.el.setHeight(box.height);
35626         }
35627         if(this.collapsed){
35628             this.updateBody(null, box.height);
35629         }
35630         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35631     }
35632 });
35633 /*
35634  * Based on:
35635  * Ext JS Library 1.1.1
35636  * Copyright(c) 2006-2007, Ext JS, LLC.
35637  *
35638  * Originally Released Under LGPL - original licence link has changed is not relivant.
35639  *
35640  * Fork - LGPL
35641  * <script type="text/javascript">
35642  */
35643  
35644  
35645 /*
35646  * Private internal class for reading and applying state
35647  */
35648 Roo.LayoutStateManager = function(layout){
35649      // default empty state
35650      this.state = {
35651         north: {},
35652         south: {},
35653         east: {},
35654         west: {}       
35655     };
35656 };
35657
35658 Roo.LayoutStateManager.prototype = {
35659     init : function(layout, provider){
35660         this.provider = provider;
35661         var state = provider.get(layout.id+"-layout-state");
35662         if(state){
35663             var wasUpdating = layout.isUpdating();
35664             if(!wasUpdating){
35665                 layout.beginUpdate();
35666             }
35667             for(var key in state){
35668                 if(typeof state[key] != "function"){
35669                     var rstate = state[key];
35670                     var r = layout.getRegion(key);
35671                     if(r && rstate){
35672                         if(rstate.size){
35673                             r.resizeTo(rstate.size);
35674                         }
35675                         if(rstate.collapsed == true){
35676                             r.collapse(true);
35677                         }else{
35678                             r.expand(null, true);
35679                         }
35680                     }
35681                 }
35682             }
35683             if(!wasUpdating){
35684                 layout.endUpdate();
35685             }
35686             this.state = state; 
35687         }
35688         this.layout = layout;
35689         layout.on("regionresized", this.onRegionResized, this);
35690         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35691         layout.on("regionexpanded", this.onRegionExpanded, this);
35692     },
35693     
35694     storeState : function(){
35695         this.provider.set(this.layout.id+"-layout-state", this.state);
35696     },
35697     
35698     onRegionResized : function(region, newSize){
35699         this.state[region.getPosition()].size = newSize;
35700         this.storeState();
35701     },
35702     
35703     onRegionCollapsed : function(region){
35704         this.state[region.getPosition()].collapsed = true;
35705         this.storeState();
35706     },
35707     
35708     onRegionExpanded : function(region){
35709         this.state[region.getPosition()].collapsed = false;
35710         this.storeState();
35711     }
35712 };/*
35713  * Based on:
35714  * Ext JS Library 1.1.1
35715  * Copyright(c) 2006-2007, Ext JS, LLC.
35716  *
35717  * Originally Released Under LGPL - original licence link has changed is not relivant.
35718  *
35719  * Fork - LGPL
35720  * <script type="text/javascript">
35721  */
35722 /**
35723  * @class Roo.ContentPanel
35724  * @extends Roo.util.Observable
35725  * @children Roo.form.Form Roo.JsonView Roo.View
35726  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35727  * A basic ContentPanel element.
35728  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35729  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35730  * @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
35731  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35732  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35733  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35734  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
35735  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35736  * @cfg {String} title          The title for this panel
35737  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35738  * @cfg {String} url            Calls {@link #setUrl} with this value
35739  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35740  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35741  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35742  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35743  * @cfg {String}    style  Extra style to add to the content panel
35744  * @cfg {Roo.menu.Menu} menu  popup menu
35745
35746  * @constructor
35747  * Create a new ContentPanel.
35748  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35749  * @param {String/Object} config A string to set only the title or a config object
35750  * @param {String} content (optional) Set the HTML content for this panel
35751  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35752  */
35753 Roo.ContentPanel = function(el, config, content){
35754     
35755      
35756     /*
35757     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35758         config = el;
35759         el = Roo.id();
35760     }
35761     if (config && config.parentLayout) { 
35762         el = config.parentLayout.el.createChild(); 
35763     }
35764     */
35765     if(el.autoCreate){ // xtype is available if this is called from factory
35766         config = el;
35767         el = Roo.id();
35768     }
35769     this.el = Roo.get(el);
35770     if(!this.el && config && config.autoCreate){
35771         if(typeof config.autoCreate == "object"){
35772             if(!config.autoCreate.id){
35773                 config.autoCreate.id = config.id||el;
35774             }
35775             this.el = Roo.DomHelper.append(document.body,
35776                         config.autoCreate, true);
35777         }else{
35778             this.el = Roo.DomHelper.append(document.body,
35779                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35780         }
35781     }
35782     
35783     
35784     this.closable = false;
35785     this.loaded = false;
35786     this.active = false;
35787     if(typeof config == "string"){
35788         this.title = config;
35789     }else{
35790         Roo.apply(this, config);
35791     }
35792     
35793     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35794         this.wrapEl = this.el.wrap();
35795         this.toolbar.container = this.el.insertSibling(false, 'before');
35796         this.toolbar = new Roo.Toolbar(this.toolbar);
35797     }
35798     
35799     // xtype created footer. - not sure if will work as we normally have to render first..
35800     if (this.footer && !this.footer.el && this.footer.xtype) {
35801         if (!this.wrapEl) {
35802             this.wrapEl = this.el.wrap();
35803         }
35804     
35805         this.footer.container = this.wrapEl.createChild();
35806          
35807         this.footer = Roo.factory(this.footer, Roo);
35808         
35809     }
35810     
35811     if(this.resizeEl){
35812         this.resizeEl = Roo.get(this.resizeEl, true);
35813     }else{
35814         this.resizeEl = this.el;
35815     }
35816     // handle view.xtype
35817     
35818  
35819     
35820     
35821     this.addEvents({
35822         /**
35823          * @event activate
35824          * Fires when this panel is activated. 
35825          * @param {Roo.ContentPanel} this
35826          */
35827         "activate" : true,
35828         /**
35829          * @event deactivate
35830          * Fires when this panel is activated. 
35831          * @param {Roo.ContentPanel} this
35832          */
35833         "deactivate" : true,
35834
35835         /**
35836          * @event resize
35837          * Fires when this panel is resized if fitToFrame is true.
35838          * @param {Roo.ContentPanel} this
35839          * @param {Number} width The width after any component adjustments
35840          * @param {Number} height The height after any component adjustments
35841          */
35842         "resize" : true,
35843         
35844          /**
35845          * @event render
35846          * Fires when this tab is created
35847          * @param {Roo.ContentPanel} this
35848          */
35849         "render" : true
35850          
35851         
35852     });
35853     
35854
35855     
35856     
35857     if(this.autoScroll){
35858         this.resizeEl.setStyle("overflow", "auto");
35859     } else {
35860         // fix randome scrolling
35861         this.el.on('scroll', function() {
35862             Roo.log('fix random scolling');
35863             this.scrollTo('top',0); 
35864         });
35865     }
35866     content = content || this.content;
35867     if(content){
35868         this.setContent(content);
35869     }
35870     if(config && config.url){
35871         this.setUrl(this.url, this.params, this.loadOnce);
35872     }
35873     
35874     
35875     
35876     Roo.ContentPanel.superclass.constructor.call(this);
35877     
35878     if (this.view && typeof(this.view.xtype) != 'undefined') {
35879         this.view.el = this.el.appendChild(document.createElement("div"));
35880         this.view = Roo.factory(this.view); 
35881         this.view.render  &&  this.view.render(false, '');  
35882     }
35883     
35884     
35885     this.fireEvent('render', this);
35886 };
35887
35888 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35889     tabTip:'',
35890     setRegion : function(region){
35891         this.region = region;
35892         if(region){
35893            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35894         }else{
35895            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35896         } 
35897     },
35898     
35899     /**
35900      * Returns the toolbar for this Panel if one was configured. 
35901      * @return {Roo.Toolbar} 
35902      */
35903     getToolbar : function(){
35904         return this.toolbar;
35905     },
35906     
35907     setActiveState : function(active){
35908         this.active = active;
35909         if(!active){
35910             this.fireEvent("deactivate", this);
35911         }else{
35912             this.fireEvent("activate", this);
35913         }
35914     },
35915     /**
35916      * Updates this panel's element
35917      * @param {String} content The new content
35918      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35919     */
35920     setContent : function(content, loadScripts){
35921         this.el.update(content, loadScripts);
35922     },
35923
35924     ignoreResize : function(w, h){
35925         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35926             return true;
35927         }else{
35928             this.lastSize = {width: w, height: h};
35929             return false;
35930         }
35931     },
35932     /**
35933      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35934      * @return {Roo.UpdateManager} The UpdateManager
35935      */
35936     getUpdateManager : function(){
35937         return this.el.getUpdateManager();
35938     },
35939      /**
35940      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35941      * @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:
35942 <pre><code>
35943 panel.load({
35944     url: "your-url.php",
35945     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35946     callback: yourFunction,
35947     scope: yourObject, //(optional scope)
35948     discardUrl: false,
35949     nocache: false,
35950     text: "Loading...",
35951     timeout: 30,
35952     scripts: false
35953 });
35954 </code></pre>
35955      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35956      * 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.
35957      * @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}
35958      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35959      * @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.
35960      * @return {Roo.ContentPanel} this
35961      */
35962     load : function(){
35963         var um = this.el.getUpdateManager();
35964         um.update.apply(um, arguments);
35965         return this;
35966     },
35967
35968
35969     /**
35970      * 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.
35971      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35972      * @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)
35973      * @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)
35974      * @return {Roo.UpdateManager} The UpdateManager
35975      */
35976     setUrl : function(url, params, loadOnce){
35977         if(this.refreshDelegate){
35978             this.removeListener("activate", this.refreshDelegate);
35979         }
35980         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35981         this.on("activate", this.refreshDelegate);
35982         return this.el.getUpdateManager();
35983     },
35984     
35985     _handleRefresh : function(url, params, loadOnce){
35986         if(!loadOnce || !this.loaded){
35987             var updater = this.el.getUpdateManager();
35988             updater.update(url, params, this._setLoaded.createDelegate(this));
35989         }
35990     },
35991     
35992     _setLoaded : function(){
35993         this.loaded = true;
35994     }, 
35995     
35996     /**
35997      * Returns this panel's id
35998      * @return {String} 
35999      */
36000     getId : function(){
36001         return this.el.id;
36002     },
36003     
36004     /** 
36005      * Returns this panel's element - used by regiosn to add.
36006      * @return {Roo.Element} 
36007      */
36008     getEl : function(){
36009         return this.wrapEl || this.el;
36010     },
36011     
36012     adjustForComponents : function(width, height)
36013     {
36014         //Roo.log('adjustForComponents ');
36015         if(this.resizeEl != this.el){
36016             width -= this.el.getFrameWidth('lr');
36017             height -= this.el.getFrameWidth('tb');
36018         }
36019         if(this.toolbar){
36020             var te = this.toolbar.getEl();
36021             height -= te.getHeight();
36022             te.setWidth(width);
36023         }
36024         if(this.footer){
36025             var te = this.footer.getEl();
36026             //Roo.log("footer:" + te.getHeight());
36027             
36028             height -= te.getHeight();
36029             te.setWidth(width);
36030         }
36031         
36032         
36033         if(this.adjustments){
36034             width += this.adjustments[0];
36035             height += this.adjustments[1];
36036         }
36037         return {"width": width, "height": height};
36038     },
36039     
36040     setSize : function(width, height){
36041         if(this.fitToFrame && !this.ignoreResize(width, height)){
36042             if(this.fitContainer && this.resizeEl != this.el){
36043                 this.el.setSize(width, height);
36044             }
36045             var size = this.adjustForComponents(width, height);
36046             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36047             this.fireEvent('resize', this, size.width, size.height);
36048         }
36049     },
36050     
36051     /**
36052      * Returns this panel's title
36053      * @return {String} 
36054      */
36055     getTitle : function(){
36056         return this.title;
36057     },
36058     
36059     /**
36060      * Set this panel's title
36061      * @param {String} title
36062      */
36063     setTitle : function(title){
36064         this.title = title;
36065         if(this.region){
36066             this.region.updatePanelTitle(this, title);
36067         }
36068     },
36069     
36070     /**
36071      * Returns true is this panel was configured to be closable
36072      * @return {Boolean} 
36073      */
36074     isClosable : function(){
36075         return this.closable;
36076     },
36077     
36078     beforeSlide : function(){
36079         this.el.clip();
36080         this.resizeEl.clip();
36081     },
36082     
36083     afterSlide : function(){
36084         this.el.unclip();
36085         this.resizeEl.unclip();
36086     },
36087     
36088     /**
36089      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36090      *   Will fail silently if the {@link #setUrl} method has not been called.
36091      *   This does not activate the panel, just updates its content.
36092      */
36093     refresh : function(){
36094         if(this.refreshDelegate){
36095            this.loaded = false;
36096            this.refreshDelegate();
36097         }
36098     },
36099     
36100     /**
36101      * Destroys this panel
36102      */
36103     destroy : function(){
36104         this.el.removeAllListeners();
36105         var tempEl = document.createElement("span");
36106         tempEl.appendChild(this.el.dom);
36107         tempEl.innerHTML = "";
36108         this.el.remove();
36109         this.el = null;
36110     },
36111     
36112     /**
36113      * form - if the content panel contains a form - this is a reference to it.
36114      * @type {Roo.form.Form}
36115      */
36116     form : false,
36117     /**
36118      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36119      *    This contains a reference to it.
36120      * @type {Roo.View}
36121      */
36122     view : false,
36123     
36124       /**
36125      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36126      * <pre><code>
36127
36128 layout.addxtype({
36129        xtype : 'Form',
36130        items: [ .... ]
36131    }
36132 );
36133
36134 </code></pre>
36135      * @param {Object} cfg Xtype definition of item to add.
36136      */
36137     
36138     addxtype : function(cfg) {
36139         // add form..
36140         if (cfg.xtype.match(/^Form$/)) {
36141             
36142             var el;
36143             //if (this.footer) {
36144             //    el = this.footer.container.insertSibling(false, 'before');
36145             //} else {
36146                 el = this.el.createChild();
36147             //}
36148
36149             this.form = new  Roo.form.Form(cfg);
36150             
36151             
36152             if ( this.form.allItems.length) {
36153                 this.form.render(el.dom);
36154             }
36155             return this.form;
36156         }
36157         // should only have one of theses..
36158         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36159             // views.. should not be just added - used named prop 'view''
36160             
36161             cfg.el = this.el.appendChild(document.createElement("div"));
36162             // factory?
36163             
36164             var ret = new Roo.factory(cfg);
36165              
36166              ret.render && ret.render(false, ''); // render blank..
36167             this.view = ret;
36168             return ret;
36169         }
36170         return false;
36171     }
36172 });
36173
36174
36175
36176
36177
36178
36179
36180
36181
36182
36183
36184
36185 /**
36186  * @class Roo.GridPanel
36187  * @extends Roo.ContentPanel
36188  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36189  * @constructor
36190  * Create a new GridPanel.
36191  * @cfg {Roo.grid.Grid} grid The grid for this panel
36192  */
36193 Roo.GridPanel = function(grid, config){
36194     
36195     // universal ctor...
36196     if (typeof(grid.grid) != 'undefined') {
36197         config = grid;
36198         grid = config.grid;
36199     }
36200     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36201         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36202         
36203     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36204     
36205     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36206     
36207     if(this.toolbar){
36208         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36209     }
36210     // xtype created footer. - not sure if will work as we normally have to render first..
36211     if (this.footer && !this.footer.el && this.footer.xtype) {
36212         
36213         this.footer.container = this.grid.getView().getFooterPanel(true);
36214         this.footer.dataSource = this.grid.dataSource;
36215         this.footer = Roo.factory(this.footer, Roo);
36216         
36217     }
36218     
36219     grid.monitorWindowResize = false; // turn off autosizing
36220     grid.autoHeight = false;
36221     grid.autoWidth = false;
36222     this.grid = grid;
36223     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36224 };
36225
36226 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36227     getId : function(){
36228         return this.grid.id;
36229     },
36230     
36231     /**
36232      * Returns the grid for this panel
36233      * @return {Roo.grid.Grid} 
36234      */
36235     getGrid : function(){
36236         return this.grid;    
36237     },
36238     
36239     setSize : function(width, height){
36240         if(!this.ignoreResize(width, height)){
36241             var grid = this.grid;
36242             var size = this.adjustForComponents(width, height);
36243             grid.getGridEl().setSize(size.width, size.height);
36244             grid.autoSize();
36245         }
36246     },
36247     
36248     beforeSlide : function(){
36249         this.grid.getView().scroller.clip();
36250     },
36251     
36252     afterSlide : function(){
36253         this.grid.getView().scroller.unclip();
36254     },
36255     
36256     destroy : function(){
36257         this.grid.destroy();
36258         delete this.grid;
36259         Roo.GridPanel.superclass.destroy.call(this); 
36260     }
36261 });
36262
36263
36264 /**
36265  * @class Roo.NestedLayoutPanel
36266  * @extends Roo.ContentPanel
36267  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36268  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
36269  *
36270  * 
36271  * @constructor
36272  * Create a new NestedLayoutPanel.
36273  * 
36274  * 
36275  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36276  * @param {String/Object} config A string to set only the title or a config object
36277  */
36278 Roo.NestedLayoutPanel = function(layout, config)
36279 {
36280     // construct with only one argument..
36281     /* FIXME - implement nicer consturctors
36282     if (layout.layout) {
36283         config = layout;
36284         layout = config.layout;
36285         delete config.layout;
36286     }
36287     if (layout.xtype && !layout.getEl) {
36288         // then layout needs constructing..
36289         layout = Roo.factory(layout, Roo);
36290     }
36291     */
36292     
36293     
36294     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36295     
36296     layout.monitorWindowResize = false; // turn off autosizing
36297     this.layout = layout;
36298     this.layout.getEl().addClass("x-layout-nested-layout");
36299     
36300     
36301     
36302     
36303 };
36304
36305 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36306
36307     layout : false,
36308
36309     setSize : function(width, height){
36310         if(!this.ignoreResize(width, height)){
36311             var size = this.adjustForComponents(width, height);
36312             var el = this.layout.getEl();
36313             el.setSize(size.width, size.height);
36314             var touch = el.dom.offsetWidth;
36315             this.layout.layout();
36316             // ie requires a double layout on the first pass
36317             if(Roo.isIE && !this.initialized){
36318                 this.initialized = true;
36319                 this.layout.layout();
36320             }
36321         }
36322     },
36323     
36324     // activate all subpanels if not currently active..
36325     
36326     setActiveState : function(active){
36327         this.active = active;
36328         if(!active){
36329             this.fireEvent("deactivate", this);
36330             return;
36331         }
36332         
36333         this.fireEvent("activate", this);
36334         // not sure if this should happen before or after..
36335         if (!this.layout) {
36336             return; // should not happen..
36337         }
36338         var reg = false;
36339         for (var r in this.layout.regions) {
36340             reg = this.layout.getRegion(r);
36341             if (reg.getActivePanel()) {
36342                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36343                 reg.setActivePanel(reg.getActivePanel());
36344                 continue;
36345             }
36346             if (!reg.panels.length) {
36347                 continue;
36348             }
36349             reg.showPanel(reg.getPanel(0));
36350         }
36351         
36352         
36353         
36354         
36355     },
36356     
36357     /**
36358      * Returns the nested BorderLayout for this panel
36359      * @return {Roo.BorderLayout}
36360      */
36361     getLayout : function(){
36362         return this.layout;
36363     },
36364     
36365      /**
36366      * Adds a xtype elements to the layout of the nested panel
36367      * <pre><code>
36368
36369 panel.addxtype({
36370        xtype : 'ContentPanel',
36371        region: 'west',
36372        items: [ .... ]
36373    }
36374 );
36375
36376 panel.addxtype({
36377         xtype : 'NestedLayoutPanel',
36378         region: 'west',
36379         layout: {
36380            center: { },
36381            west: { }   
36382         },
36383         items : [ ... list of content panels or nested layout panels.. ]
36384    }
36385 );
36386 </code></pre>
36387      * @param {Object} cfg Xtype definition of item to add.
36388      */
36389     addxtype : function(cfg) {
36390         return this.layout.addxtype(cfg);
36391     
36392     }
36393 });
36394
36395 Roo.ScrollPanel = function(el, config, content){
36396     config = config || {};
36397     config.fitToFrame = true;
36398     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36399     
36400     this.el.dom.style.overflow = "hidden";
36401     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36402     this.el.removeClass("x-layout-inactive-content");
36403     this.el.on("mousewheel", this.onWheel, this);
36404
36405     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36406     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36407     up.unselectable(); down.unselectable();
36408     up.on("click", this.scrollUp, this);
36409     down.on("click", this.scrollDown, this);
36410     up.addClassOnOver("x-scroller-btn-over");
36411     down.addClassOnOver("x-scroller-btn-over");
36412     up.addClassOnClick("x-scroller-btn-click");
36413     down.addClassOnClick("x-scroller-btn-click");
36414     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36415
36416     this.resizeEl = this.el;
36417     this.el = wrap; this.up = up; this.down = down;
36418 };
36419
36420 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36421     increment : 100,
36422     wheelIncrement : 5,
36423     scrollUp : function(){
36424         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36425     },
36426
36427     scrollDown : function(){
36428         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36429     },
36430
36431     afterScroll : function(){
36432         var el = this.resizeEl;
36433         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36434         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36435         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36436     },
36437
36438     setSize : function(){
36439         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36440         this.afterScroll();
36441     },
36442
36443     onWheel : function(e){
36444         var d = e.getWheelDelta();
36445         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36446         this.afterScroll();
36447         e.stopEvent();
36448     },
36449
36450     setContent : function(content, loadScripts){
36451         this.resizeEl.update(content, loadScripts);
36452     }
36453
36454 });
36455
36456
36457
36458 /**
36459  * @class Roo.TreePanel
36460  * @extends Roo.ContentPanel
36461  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36462  * Treepanel component
36463  * 
36464  * @constructor
36465  * Create a new TreePanel. - defaults to fit/scoll contents.
36466  * @param {String/Object} config A string to set only the panel's title, or a config object
36467  */
36468 Roo.TreePanel = function(config){
36469     var el = config.el;
36470     var tree = config.tree;
36471     delete config.tree; 
36472     delete config.el; // hopefull!
36473     
36474     // wrapper for IE7 strict & safari scroll issue
36475     
36476     var treeEl = el.createChild();
36477     config.resizeEl = treeEl;
36478     
36479     
36480     
36481     Roo.TreePanel.superclass.constructor.call(this, el, config);
36482  
36483  
36484     this.tree = new Roo.tree.TreePanel(treeEl , tree);
36485     //console.log(tree);
36486     this.on('activate', function()
36487     {
36488         if (this.tree.rendered) {
36489             return;
36490         }
36491         //console.log('render tree');
36492         this.tree.render();
36493     });
36494     // this should not be needed.. - it's actually the 'el' that resizes?
36495     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36496     
36497     //this.on('resize',  function (cp, w, h) {
36498     //        this.tree.innerCt.setWidth(w);
36499     //        this.tree.innerCt.setHeight(h);
36500     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
36501     //});
36502
36503         
36504     
36505 };
36506
36507 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
36508     fitToFrame : true,
36509     autoScroll : true,
36510     /*
36511      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36512      */
36513     tree : false
36514
36515 });
36516 /*
36517  * Based on:
36518  * Ext JS Library 1.1.1
36519  * Copyright(c) 2006-2007, Ext JS, LLC.
36520  *
36521  * Originally Released Under LGPL - original licence link has changed is not relivant.
36522  *
36523  * Fork - LGPL
36524  * <script type="text/javascript">
36525  */
36526  
36527
36528 /**
36529  * @class Roo.ReaderLayout
36530  * @extends Roo.BorderLayout
36531  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
36532  * center region containing two nested regions (a top one for a list view and one for item preview below),
36533  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36534  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36535  * expedites the setup of the overall layout and regions for this common application style.
36536  * Example:
36537  <pre><code>
36538 var reader = new Roo.ReaderLayout();
36539 var CP = Roo.ContentPanel;  // shortcut for adding
36540
36541 reader.beginUpdate();
36542 reader.add("north", new CP("north", "North"));
36543 reader.add("west", new CP("west", {title: "West"}));
36544 reader.add("east", new CP("east", {title: "East"}));
36545
36546 reader.regions.listView.add(new CP("listView", "List"));
36547 reader.regions.preview.add(new CP("preview", "Preview"));
36548 reader.endUpdate();
36549 </code></pre>
36550 * @constructor
36551 * Create a new ReaderLayout
36552 * @param {Object} config Configuration options
36553 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36554 * document.body if omitted)
36555 */
36556 Roo.ReaderLayout = function(config, renderTo){
36557     var c = config || {size:{}};
36558     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36559         north: c.north !== false ? Roo.apply({
36560             split:false,
36561             initialSize: 32,
36562             titlebar: false
36563         }, c.north) : false,
36564         west: c.west !== false ? Roo.apply({
36565             split:true,
36566             initialSize: 200,
36567             minSize: 175,
36568             maxSize: 400,
36569             titlebar: true,
36570             collapsible: true,
36571             animate: true,
36572             margins:{left:5,right:0,bottom:5,top:5},
36573             cmargins:{left:5,right:5,bottom:5,top:5}
36574         }, c.west) : false,
36575         east: c.east !== false ? Roo.apply({
36576             split:true,
36577             initialSize: 200,
36578             minSize: 175,
36579             maxSize: 400,
36580             titlebar: true,
36581             collapsible: true,
36582             animate: true,
36583             margins:{left:0,right:5,bottom:5,top:5},
36584             cmargins:{left:5,right:5,bottom:5,top:5}
36585         }, c.east) : false,
36586         center: Roo.apply({
36587             tabPosition: 'top',
36588             autoScroll:false,
36589             closeOnTab: true,
36590             titlebar:false,
36591             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36592         }, c.center)
36593     });
36594
36595     this.el.addClass('x-reader');
36596
36597     this.beginUpdate();
36598
36599     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36600         south: c.preview !== false ? Roo.apply({
36601             split:true,
36602             initialSize: 200,
36603             minSize: 100,
36604             autoScroll:true,
36605             collapsible:true,
36606             titlebar: true,
36607             cmargins:{top:5,left:0, right:0, bottom:0}
36608         }, c.preview) : false,
36609         center: Roo.apply({
36610             autoScroll:false,
36611             titlebar:false,
36612             minHeight:200
36613         }, c.listView)
36614     });
36615     this.add('center', new Roo.NestedLayoutPanel(inner,
36616             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36617
36618     this.endUpdate();
36619
36620     this.regions.preview = inner.getRegion('south');
36621     this.regions.listView = inner.getRegion('center');
36622 };
36623
36624 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36625  * Based on:
36626  * Ext JS Library 1.1.1
36627  * Copyright(c) 2006-2007, Ext JS, LLC.
36628  *
36629  * Originally Released Under LGPL - original licence link has changed is not relivant.
36630  *
36631  * Fork - LGPL
36632  * <script type="text/javascript">
36633  */
36634  
36635 /**
36636  * @class Roo.grid.Grid
36637  * @extends Roo.util.Observable
36638  * This class represents the primary interface of a component based grid control.
36639  * <br><br>Usage:<pre><code>
36640  var grid = new Roo.grid.Grid("my-container-id", {
36641      ds: myDataStore,
36642      cm: myColModel,
36643      selModel: mySelectionModel,
36644      autoSizeColumns: true,
36645      monitorWindowResize: false,
36646      trackMouseOver: true
36647  });
36648  // set any options
36649  grid.render();
36650  * </code></pre>
36651  * <b>Common Problems:</b><br/>
36652  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36653  * element will correct this<br/>
36654  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36655  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36656  * are unpredictable.<br/>
36657  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36658  * grid to calculate dimensions/offsets.<br/>
36659   * @constructor
36660  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36661  * The container MUST have some type of size defined for the grid to fill. The container will be
36662  * automatically set to position relative if it isn't already.
36663  * @param {Object} config A config object that sets properties on this grid.
36664  */
36665 Roo.grid.Grid = function(container, config){
36666         // initialize the container
36667         this.container = Roo.get(container);
36668         this.container.update("");
36669         this.container.setStyle("overflow", "hidden");
36670     this.container.addClass('x-grid-container');
36671
36672     this.id = this.container.id;
36673
36674     Roo.apply(this, config);
36675     // check and correct shorthanded configs
36676     if(this.ds){
36677         this.dataSource = this.ds;
36678         delete this.ds;
36679     }
36680     if(this.cm){
36681         this.colModel = this.cm;
36682         delete this.cm;
36683     }
36684     if(this.sm){
36685         this.selModel = this.sm;
36686         delete this.sm;
36687     }
36688
36689     if (this.selModel) {
36690         this.selModel = Roo.factory(this.selModel, Roo.grid);
36691         this.sm = this.selModel;
36692         this.sm.xmodule = this.xmodule || false;
36693     }
36694     if (typeof(this.colModel.config) == 'undefined') {
36695         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36696         this.cm = this.colModel;
36697         this.cm.xmodule = this.xmodule || false;
36698     }
36699     if (this.dataSource) {
36700         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36701         this.ds = this.dataSource;
36702         this.ds.xmodule = this.xmodule || false;
36703          
36704     }
36705     
36706     
36707     
36708     if(this.width){
36709         this.container.setWidth(this.width);
36710     }
36711
36712     if(this.height){
36713         this.container.setHeight(this.height);
36714     }
36715     /** @private */
36716         this.addEvents({
36717         // raw events
36718         /**
36719          * @event click
36720          * The raw click event for the entire grid.
36721          * @param {Roo.EventObject} e
36722          */
36723         "click" : true,
36724         /**
36725          * @event dblclick
36726          * The raw dblclick event for the entire grid.
36727          * @param {Roo.EventObject} e
36728          */
36729         "dblclick" : true,
36730         /**
36731          * @event contextmenu
36732          * The raw contextmenu event for the entire grid.
36733          * @param {Roo.EventObject} e
36734          */
36735         "contextmenu" : true,
36736         /**
36737          * @event mousedown
36738          * The raw mousedown event for the entire grid.
36739          * @param {Roo.EventObject} e
36740          */
36741         "mousedown" : true,
36742         /**
36743          * @event mouseup
36744          * The raw mouseup event for the entire grid.
36745          * @param {Roo.EventObject} e
36746          */
36747         "mouseup" : true,
36748         /**
36749          * @event mouseover
36750          * The raw mouseover event for the entire grid.
36751          * @param {Roo.EventObject} e
36752          */
36753         "mouseover" : true,
36754         /**
36755          * @event mouseout
36756          * The raw mouseout event for the entire grid.
36757          * @param {Roo.EventObject} e
36758          */
36759         "mouseout" : true,
36760         /**
36761          * @event keypress
36762          * The raw keypress event for the entire grid.
36763          * @param {Roo.EventObject} e
36764          */
36765         "keypress" : true,
36766         /**
36767          * @event keydown
36768          * The raw keydown event for the entire grid.
36769          * @param {Roo.EventObject} e
36770          */
36771         "keydown" : true,
36772
36773         // custom events
36774
36775         /**
36776          * @event cellclick
36777          * Fires when a cell is clicked
36778          * @param {Grid} this
36779          * @param {Number} rowIndex
36780          * @param {Number} columnIndex
36781          * @param {Roo.EventObject} e
36782          */
36783         "cellclick" : true,
36784         /**
36785          * @event celldblclick
36786          * Fires when a cell is double clicked
36787          * @param {Grid} this
36788          * @param {Number} rowIndex
36789          * @param {Number} columnIndex
36790          * @param {Roo.EventObject} e
36791          */
36792         "celldblclick" : true,
36793         /**
36794          * @event rowclick
36795          * Fires when a row is clicked
36796          * @param {Grid} this
36797          * @param {Number} rowIndex
36798          * @param {Roo.EventObject} e
36799          */
36800         "rowclick" : true,
36801         /**
36802          * @event rowdblclick
36803          * Fires when a row is double clicked
36804          * @param {Grid} this
36805          * @param {Number} rowIndex
36806          * @param {Roo.EventObject} e
36807          */
36808         "rowdblclick" : true,
36809         /**
36810          * @event headerclick
36811          * Fires when a header is clicked
36812          * @param {Grid} this
36813          * @param {Number} columnIndex
36814          * @param {Roo.EventObject} e
36815          */
36816         "headerclick" : true,
36817         /**
36818          * @event headerdblclick
36819          * Fires when a header cell is double clicked
36820          * @param {Grid} this
36821          * @param {Number} columnIndex
36822          * @param {Roo.EventObject} e
36823          */
36824         "headerdblclick" : true,
36825         /**
36826          * @event rowcontextmenu
36827          * Fires when a row is right clicked
36828          * @param {Grid} this
36829          * @param {Number} rowIndex
36830          * @param {Roo.EventObject} e
36831          */
36832         "rowcontextmenu" : true,
36833         /**
36834          * @event cellcontextmenu
36835          * Fires when a cell is right clicked
36836          * @param {Grid} this
36837          * @param {Number} rowIndex
36838          * @param {Number} cellIndex
36839          * @param {Roo.EventObject} e
36840          */
36841          "cellcontextmenu" : true,
36842         /**
36843          * @event headercontextmenu
36844          * Fires when a header is right clicked
36845          * @param {Grid} this
36846          * @param {Number} columnIndex
36847          * @param {Roo.EventObject} e
36848          */
36849         "headercontextmenu" : true,
36850         /**
36851          * @event bodyscroll
36852          * Fires when the body element is scrolled
36853          * @param {Number} scrollLeft
36854          * @param {Number} scrollTop
36855          */
36856         "bodyscroll" : true,
36857         /**
36858          * @event columnresize
36859          * Fires when the user resizes a column
36860          * @param {Number} columnIndex
36861          * @param {Number} newSize
36862          */
36863         "columnresize" : true,
36864         /**
36865          * @event columnmove
36866          * Fires when the user moves a column
36867          * @param {Number} oldIndex
36868          * @param {Number} newIndex
36869          */
36870         "columnmove" : true,
36871         /**
36872          * @event startdrag
36873          * Fires when row(s) start being dragged
36874          * @param {Grid} this
36875          * @param {Roo.GridDD} dd The drag drop object
36876          * @param {event} e The raw browser event
36877          */
36878         "startdrag" : true,
36879         /**
36880          * @event enddrag
36881          * Fires when a drag operation is complete
36882          * @param {Grid} this
36883          * @param {Roo.GridDD} dd The drag drop object
36884          * @param {event} e The raw browser event
36885          */
36886         "enddrag" : true,
36887         /**
36888          * @event dragdrop
36889          * Fires when dragged row(s) are dropped on a valid DD target
36890          * @param {Grid} this
36891          * @param {Roo.GridDD} dd The drag drop object
36892          * @param {String} targetId The target drag drop object
36893          * @param {event} e The raw browser event
36894          */
36895         "dragdrop" : true,
36896         /**
36897          * @event dragover
36898          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36899          * @param {Grid} this
36900          * @param {Roo.GridDD} dd The drag drop object
36901          * @param {String} targetId The target drag drop object
36902          * @param {event} e The raw browser event
36903          */
36904         "dragover" : true,
36905         /**
36906          * @event dragenter
36907          *  Fires when the dragged row(s) first cross another DD target while being dragged
36908          * @param {Grid} this
36909          * @param {Roo.GridDD} dd The drag drop object
36910          * @param {String} targetId The target drag drop object
36911          * @param {event} e The raw browser event
36912          */
36913         "dragenter" : true,
36914         /**
36915          * @event dragout
36916          * Fires when the dragged row(s) leave another DD target while being dragged
36917          * @param {Grid} this
36918          * @param {Roo.GridDD} dd The drag drop object
36919          * @param {String} targetId The target drag drop object
36920          * @param {event} e The raw browser event
36921          */
36922         "dragout" : true,
36923         /**
36924          * @event rowclass
36925          * Fires when a row is rendered, so you can change add a style to it.
36926          * @param {GridView} gridview   The grid view
36927          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36928          */
36929         'rowclass' : true,
36930
36931         /**
36932          * @event render
36933          * Fires when the grid is rendered
36934          * @param {Grid} grid
36935          */
36936         'render' : true
36937     });
36938
36939     Roo.grid.Grid.superclass.constructor.call(this);
36940 };
36941 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36942     
36943     /**
36944          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
36945          */
36946         /**
36947          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
36948          */
36949         /**
36950          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
36951          */
36952         /**
36953          * @cfg {Roo.data.Store} ds The data store for the grid
36954          */
36955         /**
36956          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
36957          */
36958         /**
36959      * @cfg {String} ddGroup - drag drop group.
36960      */
36961       /**
36962      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
36963      */
36964
36965     /**
36966      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36967      */
36968     minColumnWidth : 25,
36969
36970     /**
36971      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36972      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36973      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36974      */
36975     autoSizeColumns : false,
36976
36977     /**
36978      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36979      */
36980     autoSizeHeaders : true,
36981
36982     /**
36983      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36984      */
36985     monitorWindowResize : true,
36986
36987     /**
36988      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36989      * rows measured to get a columns size. Default is 0 (all rows).
36990      */
36991     maxRowsToMeasure : 0,
36992
36993     /**
36994      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36995      */
36996     trackMouseOver : true,
36997
36998     /**
36999     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
37000     */
37001       /**
37002     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
37003     */
37004     
37005     /**
37006     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
37007     */
37008     enableDragDrop : false,
37009     
37010     /**
37011     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
37012     */
37013     enableColumnMove : true,
37014     
37015     /**
37016     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
37017     */
37018     enableColumnHide : true,
37019     
37020     /**
37021     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
37022     */
37023     enableRowHeightSync : false,
37024     
37025     /**
37026     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
37027     */
37028     stripeRows : true,
37029     
37030     /**
37031     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
37032     */
37033     autoHeight : false,
37034
37035     /**
37036      * @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.
37037      */
37038     autoExpandColumn : false,
37039
37040     /**
37041     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
37042     * Default is 50.
37043     */
37044     autoExpandMin : 50,
37045
37046     /**
37047     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37048     */
37049     autoExpandMax : 1000,
37050
37051     /**
37052     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37053     */
37054     view : null,
37055
37056     /**
37057     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37058     */
37059     loadMask : false,
37060     /**
37061     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37062     */
37063     dropTarget: false,
37064      /**
37065     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37066     */ 
37067     sortColMenu : false,
37068     
37069     // private
37070     rendered : false,
37071
37072     /**
37073     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37074     * of a fixed width. Default is false.
37075     */
37076     /**
37077     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37078     */
37079     
37080     
37081     /**
37082     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37083     * %0 is replaced with the number of selected rows.
37084     */
37085     ddText : "{0} selected row{1}",
37086     
37087     
37088     /**
37089      * Called once after all setup has been completed and the grid is ready to be rendered.
37090      * @return {Roo.grid.Grid} this
37091      */
37092     render : function()
37093     {
37094         var c = this.container;
37095         // try to detect autoHeight/width mode
37096         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37097             this.autoHeight = true;
37098         }
37099         var view = this.getView();
37100         view.init(this);
37101
37102         c.on("click", this.onClick, this);
37103         c.on("dblclick", this.onDblClick, this);
37104         c.on("contextmenu", this.onContextMenu, this);
37105         c.on("keydown", this.onKeyDown, this);
37106         if (Roo.isTouch) {
37107             c.on("touchstart", this.onTouchStart, this);
37108         }
37109
37110         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37111
37112         this.getSelectionModel().init(this);
37113
37114         view.render();
37115
37116         if(this.loadMask){
37117             this.loadMask = new Roo.LoadMask(this.container,
37118                     Roo.apply({store:this.dataSource}, this.loadMask));
37119         }
37120         
37121         
37122         if (this.toolbar && this.toolbar.xtype) {
37123             this.toolbar.container = this.getView().getHeaderPanel(true);
37124             this.toolbar = new Roo.Toolbar(this.toolbar);
37125         }
37126         if (this.footer && this.footer.xtype) {
37127             this.footer.dataSource = this.getDataSource();
37128             this.footer.container = this.getView().getFooterPanel(true);
37129             this.footer = Roo.factory(this.footer, Roo);
37130         }
37131         if (this.dropTarget && this.dropTarget.xtype) {
37132             delete this.dropTarget.xtype;
37133             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37134         }
37135         
37136         
37137         this.rendered = true;
37138         this.fireEvent('render', this);
37139         return this;
37140     },
37141
37142     /**
37143      * Reconfigures the grid to use a different Store and Column Model.
37144      * The View will be bound to the new objects and refreshed.
37145      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37146      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37147      */
37148     reconfigure : function(dataSource, colModel){
37149         if(this.loadMask){
37150             this.loadMask.destroy();
37151             this.loadMask = new Roo.LoadMask(this.container,
37152                     Roo.apply({store:dataSource}, this.loadMask));
37153         }
37154         this.view.bind(dataSource, colModel);
37155         this.dataSource = dataSource;
37156         this.colModel = colModel;
37157         this.view.refresh(true);
37158     },
37159     /**
37160      * addColumns
37161      * Add's a column, default at the end..
37162      
37163      * @param {int} position to add (default end)
37164      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37165      */
37166     addColumns : function(pos, ar)
37167     {
37168         
37169         for (var i =0;i< ar.length;i++) {
37170             var cfg = ar[i];
37171             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37172             this.cm.lookup[cfg.id] = cfg;
37173         }
37174         
37175         
37176         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37177             pos = this.cm.config.length; //this.cm.config.push(cfg);
37178         } 
37179         pos = Math.max(0,pos);
37180         ar.unshift(0);
37181         ar.unshift(pos);
37182         this.cm.config.splice.apply(this.cm.config, ar);
37183         
37184         
37185         
37186         this.view.generateRules(this.cm);
37187         this.view.refresh(true);
37188         
37189     },
37190     
37191     
37192     
37193     
37194     // private
37195     onKeyDown : function(e){
37196         this.fireEvent("keydown", e);
37197     },
37198
37199     /**
37200      * Destroy this grid.
37201      * @param {Boolean} removeEl True to remove the element
37202      */
37203     destroy : function(removeEl, keepListeners){
37204         if(this.loadMask){
37205             this.loadMask.destroy();
37206         }
37207         var c = this.container;
37208         c.removeAllListeners();
37209         this.view.destroy();
37210         this.colModel.purgeListeners();
37211         if(!keepListeners){
37212             this.purgeListeners();
37213         }
37214         c.update("");
37215         if(removeEl === true){
37216             c.remove();
37217         }
37218     },
37219
37220     // private
37221     processEvent : function(name, e){
37222         // does this fire select???
37223         //Roo.log('grid:processEvent '  + name);
37224         
37225         if (name != 'touchstart' ) {
37226             this.fireEvent(name, e);    
37227         }
37228         
37229         var t = e.getTarget();
37230         var v = this.view;
37231         var header = v.findHeaderIndex(t);
37232         if(header !== false){
37233             var ename = name == 'touchstart' ? 'click' : name;
37234              
37235             this.fireEvent("header" + ename, this, header, e);
37236         }else{
37237             var row = v.findRowIndex(t);
37238             var cell = v.findCellIndex(t);
37239             if (name == 'touchstart') {
37240                 // first touch is always a click.
37241                 // hopefull this happens after selection is updated.?
37242                 name = false;
37243                 
37244                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37245                     var cs = this.selModel.getSelectedCell();
37246                     if (row == cs[0] && cell == cs[1]){
37247                         name = 'dblclick';
37248                     }
37249                 }
37250                 if (typeof(this.selModel.getSelections) != 'undefined') {
37251                     var cs = this.selModel.getSelections();
37252                     var ds = this.dataSource;
37253                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37254                         name = 'dblclick';
37255                     }
37256                 }
37257                 if (!name) {
37258                     return;
37259                 }
37260             }
37261             
37262             
37263             if(row !== false){
37264                 this.fireEvent("row" + name, this, row, e);
37265                 if(cell !== false){
37266                     this.fireEvent("cell" + name, this, row, cell, e);
37267                 }
37268             }
37269         }
37270     },
37271
37272     // private
37273     onClick : function(e){
37274         this.processEvent("click", e);
37275     },
37276    // private
37277     onTouchStart : function(e){
37278         this.processEvent("touchstart", e);
37279     },
37280
37281     // private
37282     onContextMenu : function(e, t){
37283         this.processEvent("contextmenu", e);
37284     },
37285
37286     // private
37287     onDblClick : function(e){
37288         this.processEvent("dblclick", e);
37289     },
37290
37291     // private
37292     walkCells : function(row, col, step, fn, scope){
37293         var cm = this.colModel, clen = cm.getColumnCount();
37294         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37295         if(step < 0){
37296             if(col < 0){
37297                 row--;
37298                 first = false;
37299             }
37300             while(row >= 0){
37301                 if(!first){
37302                     col = clen-1;
37303                 }
37304                 first = false;
37305                 while(col >= 0){
37306                     if(fn.call(scope || this, row, col, cm) === true){
37307                         return [row, col];
37308                     }
37309                     col--;
37310                 }
37311                 row--;
37312             }
37313         } else {
37314             if(col >= clen){
37315                 row++;
37316                 first = false;
37317             }
37318             while(row < rlen){
37319                 if(!first){
37320                     col = 0;
37321                 }
37322                 first = false;
37323                 while(col < clen){
37324                     if(fn.call(scope || this, row, col, cm) === true){
37325                         return [row, col];
37326                     }
37327                     col++;
37328                 }
37329                 row++;
37330             }
37331         }
37332         return null;
37333     },
37334
37335     // private
37336     getSelections : function(){
37337         return this.selModel.getSelections();
37338     },
37339
37340     /**
37341      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37342      * but if manual update is required this method will initiate it.
37343      */
37344     autoSize : function(){
37345         if(this.rendered){
37346             this.view.layout();
37347             if(this.view.adjustForScroll){
37348                 this.view.adjustForScroll();
37349             }
37350         }
37351     },
37352
37353     /**
37354      * Returns the grid's underlying element.
37355      * @return {Element} The element
37356      */
37357     getGridEl : function(){
37358         return this.container;
37359     },
37360
37361     // private for compatibility, overridden by editor grid
37362     stopEditing : function(){},
37363
37364     /**
37365      * Returns the grid's SelectionModel.
37366      * @return {SelectionModel}
37367      */
37368     getSelectionModel : function(){
37369         if(!this.selModel){
37370             this.selModel = new Roo.grid.RowSelectionModel();
37371         }
37372         return this.selModel;
37373     },
37374
37375     /**
37376      * Returns the grid's DataSource.
37377      * @return {DataSource}
37378      */
37379     getDataSource : function(){
37380         return this.dataSource;
37381     },
37382
37383     /**
37384      * Returns the grid's ColumnModel.
37385      * @return {ColumnModel}
37386      */
37387     getColumnModel : function(){
37388         return this.colModel;
37389     },
37390
37391     /**
37392      * Returns the grid's GridView object.
37393      * @return {GridView}
37394      */
37395     getView : function(){
37396         if(!this.view){
37397             this.view = new Roo.grid.GridView(this.viewConfig);
37398             this.relayEvents(this.view, [
37399                 "beforerowremoved", "beforerowsinserted",
37400                 "beforerefresh", "rowremoved",
37401                 "rowsinserted", "rowupdated" ,"refresh"
37402             ]);
37403         }
37404         return this.view;
37405     },
37406     /**
37407      * Called to get grid's drag proxy text, by default returns this.ddText.
37408      * Override this to put something different in the dragged text.
37409      * @return {String}
37410      */
37411     getDragDropText : function(){
37412         var count = this.selModel.getCount();
37413         return String.format(this.ddText, count, count == 1 ? '' : 's');
37414     }
37415 });
37416 /*
37417  * Based on:
37418  * Ext JS Library 1.1.1
37419  * Copyright(c) 2006-2007, Ext JS, LLC.
37420  *
37421  * Originally Released Under LGPL - original licence link has changed is not relivant.
37422  *
37423  * Fork - LGPL
37424  * <script type="text/javascript">
37425  */
37426  /**
37427  * @class Roo.grid.AbstractGridView
37428  * @extends Roo.util.Observable
37429  * @abstract
37430  * Abstract base class for grid Views
37431  * @constructor
37432  */
37433 Roo.grid.AbstractGridView = function(){
37434         this.grid = null;
37435         
37436         this.events = {
37437             "beforerowremoved" : true,
37438             "beforerowsinserted" : true,
37439             "beforerefresh" : true,
37440             "rowremoved" : true,
37441             "rowsinserted" : true,
37442             "rowupdated" : true,
37443             "refresh" : true
37444         };
37445     Roo.grid.AbstractGridView.superclass.constructor.call(this);
37446 };
37447
37448 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37449     rowClass : "x-grid-row",
37450     cellClass : "x-grid-cell",
37451     tdClass : "x-grid-td",
37452     hdClass : "x-grid-hd",
37453     splitClass : "x-grid-hd-split",
37454     
37455     init: function(grid){
37456         this.grid = grid;
37457                 var cid = this.grid.getGridEl().id;
37458         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37459         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37460         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37461         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37462         },
37463         
37464     getColumnRenderers : function(){
37465         var renderers = [];
37466         var cm = this.grid.colModel;
37467         var colCount = cm.getColumnCount();
37468         for(var i = 0; i < colCount; i++){
37469             renderers[i] = cm.getRenderer(i);
37470         }
37471         return renderers;
37472     },
37473     
37474     getColumnIds : function(){
37475         var ids = [];
37476         var cm = this.grid.colModel;
37477         var colCount = cm.getColumnCount();
37478         for(var i = 0; i < colCount; i++){
37479             ids[i] = cm.getColumnId(i);
37480         }
37481         return ids;
37482     },
37483     
37484     getDataIndexes : function(){
37485         if(!this.indexMap){
37486             this.indexMap = this.buildIndexMap();
37487         }
37488         return this.indexMap.colToData;
37489     },
37490     
37491     getColumnIndexByDataIndex : function(dataIndex){
37492         if(!this.indexMap){
37493             this.indexMap = this.buildIndexMap();
37494         }
37495         return this.indexMap.dataToCol[dataIndex];
37496     },
37497     
37498     /**
37499      * Set a css style for a column dynamically. 
37500      * @param {Number} colIndex The index of the column
37501      * @param {String} name The css property name
37502      * @param {String} value The css value
37503      */
37504     setCSSStyle : function(colIndex, name, value){
37505         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37506         Roo.util.CSS.updateRule(selector, name, value);
37507     },
37508     
37509     generateRules : function(cm){
37510         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37511         Roo.util.CSS.removeStyleSheet(rulesId);
37512         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37513             var cid = cm.getColumnId(i);
37514             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37515                          this.tdSelector, cid, " {\n}\n",
37516                          this.hdSelector, cid, " {\n}\n",
37517                          this.splitSelector, cid, " {\n}\n");
37518         }
37519         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37520     }
37521 });/*
37522  * Based on:
37523  * Ext JS Library 1.1.1
37524  * Copyright(c) 2006-2007, Ext JS, LLC.
37525  *
37526  * Originally Released Under LGPL - original licence link has changed is not relivant.
37527  *
37528  * Fork - LGPL
37529  * <script type="text/javascript">
37530  */
37531
37532 // private
37533 // This is a support class used internally by the Grid components
37534 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37535     this.grid = grid;
37536     this.view = grid.getView();
37537     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37538     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37539     if(hd2){
37540         this.setHandleElId(Roo.id(hd));
37541         this.setOuterHandleElId(Roo.id(hd2));
37542     }
37543     this.scroll = false;
37544 };
37545 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37546     maxDragWidth: 120,
37547     getDragData : function(e){
37548         var t = Roo.lib.Event.getTarget(e);
37549         var h = this.view.findHeaderCell(t);
37550         if(h){
37551             return {ddel: h.firstChild, header:h};
37552         }
37553         return false;
37554     },
37555
37556     onInitDrag : function(e){
37557         this.view.headersDisabled = true;
37558         var clone = this.dragData.ddel.cloneNode(true);
37559         clone.id = Roo.id();
37560         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37561         this.proxy.update(clone);
37562         return true;
37563     },
37564
37565     afterValidDrop : function(){
37566         var v = this.view;
37567         setTimeout(function(){
37568             v.headersDisabled = false;
37569         }, 50);
37570     },
37571
37572     afterInvalidDrop : function(){
37573         var v = this.view;
37574         setTimeout(function(){
37575             v.headersDisabled = false;
37576         }, 50);
37577     }
37578 });
37579 /*
37580  * Based on:
37581  * Ext JS Library 1.1.1
37582  * Copyright(c) 2006-2007, Ext JS, LLC.
37583  *
37584  * Originally Released Under LGPL - original licence link has changed is not relivant.
37585  *
37586  * Fork - LGPL
37587  * <script type="text/javascript">
37588  */
37589 // private
37590 // This is a support class used internally by the Grid components
37591 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37592     this.grid = grid;
37593     this.view = grid.getView();
37594     // split the proxies so they don't interfere with mouse events
37595     this.proxyTop = Roo.DomHelper.append(document.body, {
37596         cls:"col-move-top", html:"&#160;"
37597     }, true);
37598     this.proxyBottom = Roo.DomHelper.append(document.body, {
37599         cls:"col-move-bottom", html:"&#160;"
37600     }, true);
37601     this.proxyTop.hide = this.proxyBottom.hide = function(){
37602         this.setLeftTop(-100,-100);
37603         this.setStyle("visibility", "hidden");
37604     };
37605     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37606     // temporarily disabled
37607     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37608     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37609 };
37610 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37611     proxyOffsets : [-4, -9],
37612     fly: Roo.Element.fly,
37613
37614     getTargetFromEvent : function(e){
37615         var t = Roo.lib.Event.getTarget(e);
37616         var cindex = this.view.findCellIndex(t);
37617         if(cindex !== false){
37618             return this.view.getHeaderCell(cindex);
37619         }
37620         return null;
37621     },
37622
37623     nextVisible : function(h){
37624         var v = this.view, cm = this.grid.colModel;
37625         h = h.nextSibling;
37626         while(h){
37627             if(!cm.isHidden(v.getCellIndex(h))){
37628                 return h;
37629             }
37630             h = h.nextSibling;
37631         }
37632         return null;
37633     },
37634
37635     prevVisible : function(h){
37636         var v = this.view, cm = this.grid.colModel;
37637         h = h.prevSibling;
37638         while(h){
37639             if(!cm.isHidden(v.getCellIndex(h))){
37640                 return h;
37641             }
37642             h = h.prevSibling;
37643         }
37644         return null;
37645     },
37646
37647     positionIndicator : function(h, n, e){
37648         var x = Roo.lib.Event.getPageX(e);
37649         var r = Roo.lib.Dom.getRegion(n.firstChild);
37650         var px, pt, py = r.top + this.proxyOffsets[1];
37651         if((r.right - x) <= (r.right-r.left)/2){
37652             px = r.right+this.view.borderWidth;
37653             pt = "after";
37654         }else{
37655             px = r.left;
37656             pt = "before";
37657         }
37658         var oldIndex = this.view.getCellIndex(h);
37659         var newIndex = this.view.getCellIndex(n);
37660
37661         if(this.grid.colModel.isFixed(newIndex)){
37662             return false;
37663         }
37664
37665         var locked = this.grid.colModel.isLocked(newIndex);
37666
37667         if(pt == "after"){
37668             newIndex++;
37669         }
37670         if(oldIndex < newIndex){
37671             newIndex--;
37672         }
37673         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37674             return false;
37675         }
37676         px +=  this.proxyOffsets[0];
37677         this.proxyTop.setLeftTop(px, py);
37678         this.proxyTop.show();
37679         if(!this.bottomOffset){
37680             this.bottomOffset = this.view.mainHd.getHeight();
37681         }
37682         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37683         this.proxyBottom.show();
37684         return pt;
37685     },
37686
37687     onNodeEnter : function(n, dd, e, data){
37688         if(data.header != n){
37689             this.positionIndicator(data.header, n, e);
37690         }
37691     },
37692
37693     onNodeOver : function(n, dd, e, data){
37694         var result = false;
37695         if(data.header != n){
37696             result = this.positionIndicator(data.header, n, e);
37697         }
37698         if(!result){
37699             this.proxyTop.hide();
37700             this.proxyBottom.hide();
37701         }
37702         return result ? this.dropAllowed : this.dropNotAllowed;
37703     },
37704
37705     onNodeOut : function(n, dd, e, data){
37706         this.proxyTop.hide();
37707         this.proxyBottom.hide();
37708     },
37709
37710     onNodeDrop : function(n, dd, e, data){
37711         var h = data.header;
37712         if(h != n){
37713             var cm = this.grid.colModel;
37714             var x = Roo.lib.Event.getPageX(e);
37715             var r = Roo.lib.Dom.getRegion(n.firstChild);
37716             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37717             var oldIndex = this.view.getCellIndex(h);
37718             var newIndex = this.view.getCellIndex(n);
37719             var locked = cm.isLocked(newIndex);
37720             if(pt == "after"){
37721                 newIndex++;
37722             }
37723             if(oldIndex < newIndex){
37724                 newIndex--;
37725             }
37726             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37727                 return false;
37728             }
37729             cm.setLocked(oldIndex, locked, true);
37730             cm.moveColumn(oldIndex, newIndex);
37731             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37732             return true;
37733         }
37734         return false;
37735     }
37736 });
37737 /*
37738  * Based on:
37739  * Ext JS Library 1.1.1
37740  * Copyright(c) 2006-2007, Ext JS, LLC.
37741  *
37742  * Originally Released Under LGPL - original licence link has changed is not relivant.
37743  *
37744  * Fork - LGPL
37745  * <script type="text/javascript">
37746  */
37747   
37748 /**
37749  * @class Roo.grid.GridView
37750  * @extends Roo.util.Observable
37751  *
37752  * @constructor
37753  * @param {Object} config
37754  */
37755 Roo.grid.GridView = function(config){
37756     Roo.grid.GridView.superclass.constructor.call(this);
37757     this.el = null;
37758
37759     Roo.apply(this, config);
37760 };
37761
37762 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37763
37764     unselectable :  'unselectable="on"',
37765     unselectableCls :  'x-unselectable',
37766     
37767     
37768     rowClass : "x-grid-row",
37769
37770     cellClass : "x-grid-col",
37771
37772     tdClass : "x-grid-td",
37773
37774     hdClass : "x-grid-hd",
37775
37776     splitClass : "x-grid-split",
37777
37778     sortClasses : ["sort-asc", "sort-desc"],
37779
37780     enableMoveAnim : false,
37781
37782     hlColor: "C3DAF9",
37783
37784     dh : Roo.DomHelper,
37785
37786     fly : Roo.Element.fly,
37787
37788     css : Roo.util.CSS,
37789
37790     borderWidth: 1,
37791
37792     splitOffset: 3,
37793
37794     scrollIncrement : 22,
37795
37796     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37797
37798     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37799
37800     bind : function(ds, cm){
37801         if(this.ds){
37802             this.ds.un("load", this.onLoad, this);
37803             this.ds.un("datachanged", this.onDataChange, this);
37804             this.ds.un("add", this.onAdd, this);
37805             this.ds.un("remove", this.onRemove, this);
37806             this.ds.un("update", this.onUpdate, this);
37807             this.ds.un("clear", this.onClear, this);
37808         }
37809         if(ds){
37810             ds.on("load", this.onLoad, this);
37811             ds.on("datachanged", this.onDataChange, this);
37812             ds.on("add", this.onAdd, this);
37813             ds.on("remove", this.onRemove, this);
37814             ds.on("update", this.onUpdate, this);
37815             ds.on("clear", this.onClear, this);
37816         }
37817         this.ds = ds;
37818
37819         if(this.cm){
37820             this.cm.un("widthchange", this.onColWidthChange, this);
37821             this.cm.un("headerchange", this.onHeaderChange, this);
37822             this.cm.un("hiddenchange", this.onHiddenChange, this);
37823             this.cm.un("columnmoved", this.onColumnMove, this);
37824             this.cm.un("columnlockchange", this.onColumnLock, this);
37825         }
37826         if(cm){
37827             this.generateRules(cm);
37828             cm.on("widthchange", this.onColWidthChange, this);
37829             cm.on("headerchange", this.onHeaderChange, this);
37830             cm.on("hiddenchange", this.onHiddenChange, this);
37831             cm.on("columnmoved", this.onColumnMove, this);
37832             cm.on("columnlockchange", this.onColumnLock, this);
37833         }
37834         this.cm = cm;
37835     },
37836
37837     init: function(grid){
37838         Roo.grid.GridView.superclass.init.call(this, grid);
37839
37840         this.bind(grid.dataSource, grid.colModel);
37841
37842         grid.on("headerclick", this.handleHeaderClick, this);
37843
37844         if(grid.trackMouseOver){
37845             grid.on("mouseover", this.onRowOver, this);
37846             grid.on("mouseout", this.onRowOut, this);
37847         }
37848         grid.cancelTextSelection = function(){};
37849         this.gridId = grid.id;
37850
37851         var tpls = this.templates || {};
37852
37853         if(!tpls.master){
37854             tpls.master = new Roo.Template(
37855                '<div class="x-grid" hidefocus="true">',
37856                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37857                   '<div class="x-grid-topbar"></div>',
37858                   '<div class="x-grid-scroller"><div></div></div>',
37859                   '<div class="x-grid-locked">',
37860                       '<div class="x-grid-header">{lockedHeader}</div>',
37861                       '<div class="x-grid-body">{lockedBody}</div>',
37862                   "</div>",
37863                   '<div class="x-grid-viewport">',
37864                       '<div class="x-grid-header">{header}</div>',
37865                       '<div class="x-grid-body">{body}</div>',
37866                   "</div>",
37867                   '<div class="x-grid-bottombar"></div>',
37868                  
37869                   '<div class="x-grid-resize-proxy">&#160;</div>',
37870                "</div>"
37871             );
37872             tpls.master.disableformats = true;
37873         }
37874
37875         if(!tpls.header){
37876             tpls.header = new Roo.Template(
37877                '<table border="0" cellspacing="0" cellpadding="0">',
37878                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37879                "</table>{splits}"
37880             );
37881             tpls.header.disableformats = true;
37882         }
37883         tpls.header.compile();
37884
37885         if(!tpls.hcell){
37886             tpls.hcell = new Roo.Template(
37887                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37888                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37889                 "</div></td>"
37890              );
37891              tpls.hcell.disableFormats = true;
37892         }
37893         tpls.hcell.compile();
37894
37895         if(!tpls.hsplit){
37896             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37897                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37898             tpls.hsplit.disableFormats = true;
37899         }
37900         tpls.hsplit.compile();
37901
37902         if(!tpls.body){
37903             tpls.body = new Roo.Template(
37904                '<table border="0" cellspacing="0" cellpadding="0">',
37905                "<tbody>{rows}</tbody>",
37906                "</table>"
37907             );
37908             tpls.body.disableFormats = true;
37909         }
37910         tpls.body.compile();
37911
37912         if(!tpls.row){
37913             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37914             tpls.row.disableFormats = true;
37915         }
37916         tpls.row.compile();
37917
37918         if(!tpls.cell){
37919             tpls.cell = new Roo.Template(
37920                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37921                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37922                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37923                 "</td>"
37924             );
37925             tpls.cell.disableFormats = true;
37926         }
37927         tpls.cell.compile();
37928
37929         this.templates = tpls;
37930     },
37931
37932     // remap these for backwards compat
37933     onColWidthChange : function(){
37934         this.updateColumns.apply(this, arguments);
37935     },
37936     onHeaderChange : function(){
37937         this.updateHeaders.apply(this, arguments);
37938     }, 
37939     onHiddenChange : function(){
37940         this.handleHiddenChange.apply(this, arguments);
37941     },
37942     onColumnMove : function(){
37943         this.handleColumnMove.apply(this, arguments);
37944     },
37945     onColumnLock : function(){
37946         this.handleLockChange.apply(this, arguments);
37947     },
37948
37949     onDataChange : function(){
37950         this.refresh();
37951         this.updateHeaderSortState();
37952     },
37953
37954     onClear : function(){
37955         this.refresh();
37956     },
37957
37958     onUpdate : function(ds, record){
37959         this.refreshRow(record);
37960     },
37961
37962     refreshRow : function(record){
37963         var ds = this.ds, index;
37964         if(typeof record == 'number'){
37965             index = record;
37966             record = ds.getAt(index);
37967         }else{
37968             index = ds.indexOf(record);
37969         }
37970         this.insertRows(ds, index, index, true);
37971         this.onRemove(ds, record, index+1, true);
37972         this.syncRowHeights(index, index);
37973         this.layout();
37974         this.fireEvent("rowupdated", this, index, record);
37975     },
37976
37977     onAdd : function(ds, records, index){
37978         this.insertRows(ds, index, index + (records.length-1));
37979     },
37980
37981     onRemove : function(ds, record, index, isUpdate){
37982         if(isUpdate !== true){
37983             this.fireEvent("beforerowremoved", this, index, record);
37984         }
37985         var bt = this.getBodyTable(), lt = this.getLockedTable();
37986         if(bt.rows[index]){
37987             bt.firstChild.removeChild(bt.rows[index]);
37988         }
37989         if(lt.rows[index]){
37990             lt.firstChild.removeChild(lt.rows[index]);
37991         }
37992         if(isUpdate !== true){
37993             this.stripeRows(index);
37994             this.syncRowHeights(index, index);
37995             this.layout();
37996             this.fireEvent("rowremoved", this, index, record);
37997         }
37998     },
37999
38000     onLoad : function(){
38001         this.scrollToTop();
38002     },
38003
38004     /**
38005      * Scrolls the grid to the top
38006      */
38007     scrollToTop : function(){
38008         if(this.scroller){
38009             this.scroller.dom.scrollTop = 0;
38010             this.syncScroll();
38011         }
38012     },
38013
38014     /**
38015      * Gets a panel in the header of the grid that can be used for toolbars etc.
38016      * After modifying the contents of this panel a call to grid.autoSize() may be
38017      * required to register any changes in size.
38018      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
38019      * @return Roo.Element
38020      */
38021     getHeaderPanel : function(doShow){
38022         if(doShow){
38023             this.headerPanel.show();
38024         }
38025         return this.headerPanel;
38026     },
38027
38028     /**
38029      * Gets a panel in the footer of the grid that can be used for toolbars etc.
38030      * After modifying the contents of this panel a call to grid.autoSize() may be
38031      * required to register any changes in size.
38032      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
38033      * @return Roo.Element
38034      */
38035     getFooterPanel : function(doShow){
38036         if(doShow){
38037             this.footerPanel.show();
38038         }
38039         return this.footerPanel;
38040     },
38041
38042     initElements : function(){
38043         var E = Roo.Element;
38044         var el = this.grid.getGridEl().dom.firstChild;
38045         var cs = el.childNodes;
38046
38047         this.el = new E(el);
38048         
38049          this.focusEl = new E(el.firstChild);
38050         this.focusEl.swallowEvent("click", true);
38051         
38052         this.headerPanel = new E(cs[1]);
38053         this.headerPanel.enableDisplayMode("block");
38054
38055         this.scroller = new E(cs[2]);
38056         this.scrollSizer = new E(this.scroller.dom.firstChild);
38057
38058         this.lockedWrap = new E(cs[3]);
38059         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38060         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38061
38062         this.mainWrap = new E(cs[4]);
38063         this.mainHd = new E(this.mainWrap.dom.firstChild);
38064         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38065
38066         this.footerPanel = new E(cs[5]);
38067         this.footerPanel.enableDisplayMode("block");
38068
38069         this.resizeProxy = new E(cs[6]);
38070
38071         this.headerSelector = String.format(
38072            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38073            this.lockedHd.id, this.mainHd.id
38074         );
38075
38076         this.splitterSelector = String.format(
38077            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38078            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38079         );
38080     },
38081     idToCssName : function(s)
38082     {
38083         return s.replace(/[^a-z0-9]+/ig, '-');
38084     },
38085
38086     getHeaderCell : function(index){
38087         return Roo.DomQuery.select(this.headerSelector)[index];
38088     },
38089
38090     getHeaderCellMeasure : function(index){
38091         return this.getHeaderCell(index).firstChild;
38092     },
38093
38094     getHeaderCellText : function(index){
38095         return this.getHeaderCell(index).firstChild.firstChild;
38096     },
38097
38098     getLockedTable : function(){
38099         return this.lockedBody.dom.firstChild;
38100     },
38101
38102     getBodyTable : function(){
38103         return this.mainBody.dom.firstChild;
38104     },
38105
38106     getLockedRow : function(index){
38107         return this.getLockedTable().rows[index];
38108     },
38109
38110     getRow : function(index){
38111         return this.getBodyTable().rows[index];
38112     },
38113
38114     getRowComposite : function(index){
38115         if(!this.rowEl){
38116             this.rowEl = new Roo.CompositeElementLite();
38117         }
38118         var els = [], lrow, mrow;
38119         if(lrow = this.getLockedRow(index)){
38120             els.push(lrow);
38121         }
38122         if(mrow = this.getRow(index)){
38123             els.push(mrow);
38124         }
38125         this.rowEl.elements = els;
38126         return this.rowEl;
38127     },
38128     /**
38129      * Gets the 'td' of the cell
38130      * 
38131      * @param {Integer} rowIndex row to select
38132      * @param {Integer} colIndex column to select
38133      * 
38134      * @return {Object} 
38135      */
38136     getCell : function(rowIndex, colIndex){
38137         var locked = this.cm.getLockedCount();
38138         var source;
38139         if(colIndex < locked){
38140             source = this.lockedBody.dom.firstChild;
38141         }else{
38142             source = this.mainBody.dom.firstChild;
38143             colIndex -= locked;
38144         }
38145         return source.rows[rowIndex].childNodes[colIndex];
38146     },
38147
38148     getCellText : function(rowIndex, colIndex){
38149         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38150     },
38151
38152     getCellBox : function(cell){
38153         var b = this.fly(cell).getBox();
38154         if(Roo.isOpera){ // opera fails to report the Y
38155             b.y = cell.offsetTop + this.mainBody.getY();
38156         }
38157         return b;
38158     },
38159
38160     getCellIndex : function(cell){
38161         var id = String(cell.className).match(this.cellRE);
38162         if(id){
38163             return parseInt(id[1], 10);
38164         }
38165         return 0;
38166     },
38167
38168     findHeaderIndex : function(n){
38169         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38170         return r ? this.getCellIndex(r) : false;
38171     },
38172
38173     findHeaderCell : function(n){
38174         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38175         return r ? r : false;
38176     },
38177
38178     findRowIndex : function(n){
38179         if(!n){
38180             return false;
38181         }
38182         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38183         return r ? r.rowIndex : false;
38184     },
38185
38186     findCellIndex : function(node){
38187         var stop = this.el.dom;
38188         while(node && node != stop){
38189             if(this.findRE.test(node.className)){
38190                 return this.getCellIndex(node);
38191             }
38192             node = node.parentNode;
38193         }
38194         return false;
38195     },
38196
38197     getColumnId : function(index){
38198         return this.cm.getColumnId(index);
38199     },
38200
38201     getSplitters : function()
38202     {
38203         if(this.splitterSelector){
38204            return Roo.DomQuery.select(this.splitterSelector);
38205         }else{
38206             return null;
38207       }
38208     },
38209
38210     getSplitter : function(index){
38211         return this.getSplitters()[index];
38212     },
38213
38214     onRowOver : function(e, t){
38215         var row;
38216         if((row = this.findRowIndex(t)) !== false){
38217             this.getRowComposite(row).addClass("x-grid-row-over");
38218         }
38219     },
38220
38221     onRowOut : function(e, t){
38222         var row;
38223         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38224             this.getRowComposite(row).removeClass("x-grid-row-over");
38225         }
38226     },
38227
38228     renderHeaders : function(){
38229         var cm = this.cm;
38230         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38231         var cb = [], lb = [], sb = [], lsb = [], p = {};
38232         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38233             p.cellId = "x-grid-hd-0-" + i;
38234             p.splitId = "x-grid-csplit-0-" + i;
38235             p.id = cm.getColumnId(i);
38236             p.value = cm.getColumnHeader(i) || "";
38237             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38238             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38239             if(!cm.isLocked(i)){
38240                 cb[cb.length] = ct.apply(p);
38241                 sb[sb.length] = st.apply(p);
38242             }else{
38243                 lb[lb.length] = ct.apply(p);
38244                 lsb[lsb.length] = st.apply(p);
38245             }
38246         }
38247         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38248                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38249     },
38250
38251     updateHeaders : function(){
38252         var html = this.renderHeaders();
38253         this.lockedHd.update(html[0]);
38254         this.mainHd.update(html[1]);
38255     },
38256
38257     /**
38258      * Focuses the specified row.
38259      * @param {Number} row The row index
38260      */
38261     focusRow : function(row)
38262     {
38263         //Roo.log('GridView.focusRow');
38264         var x = this.scroller.dom.scrollLeft;
38265         this.focusCell(row, 0, false);
38266         this.scroller.dom.scrollLeft = x;
38267     },
38268
38269     /**
38270      * Focuses the specified cell.
38271      * @param {Number} row The row index
38272      * @param {Number} col The column index
38273      * @param {Boolean} hscroll false to disable horizontal scrolling
38274      */
38275     focusCell : function(row, col, hscroll)
38276     {
38277         //Roo.log('GridView.focusCell');
38278         var el = this.ensureVisible(row, col, hscroll);
38279         this.focusEl.alignTo(el, "tl-tl");
38280         if(Roo.isGecko){
38281             this.focusEl.focus();
38282         }else{
38283             this.focusEl.focus.defer(1, this.focusEl);
38284         }
38285     },
38286
38287     /**
38288      * Scrolls the specified cell into view
38289      * @param {Number} row The row index
38290      * @param {Number} col The column index
38291      * @param {Boolean} hscroll false to disable horizontal scrolling
38292      */
38293     ensureVisible : function(row, col, hscroll)
38294     {
38295         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38296         //return null; //disable for testing.
38297         if(typeof row != "number"){
38298             row = row.rowIndex;
38299         }
38300         if(row < 0 && row >= this.ds.getCount()){
38301             return  null;
38302         }
38303         col = (col !== undefined ? col : 0);
38304         var cm = this.grid.colModel;
38305         while(cm.isHidden(col)){
38306             col++;
38307         }
38308
38309         var el = this.getCell(row, col);
38310         if(!el){
38311             return null;
38312         }
38313         var c = this.scroller.dom;
38314
38315         var ctop = parseInt(el.offsetTop, 10);
38316         var cleft = parseInt(el.offsetLeft, 10);
38317         var cbot = ctop + el.offsetHeight;
38318         var cright = cleft + el.offsetWidth;
38319         
38320         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38321         var stop = parseInt(c.scrollTop, 10);
38322         var sleft = parseInt(c.scrollLeft, 10);
38323         var sbot = stop + ch;
38324         var sright = sleft + c.clientWidth;
38325         /*
38326         Roo.log('GridView.ensureVisible:' +
38327                 ' ctop:' + ctop +
38328                 ' c.clientHeight:' + c.clientHeight +
38329                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38330                 ' stop:' + stop +
38331                 ' cbot:' + cbot +
38332                 ' sbot:' + sbot +
38333                 ' ch:' + ch  
38334                 );
38335         */
38336         if(ctop < stop){
38337             c.scrollTop = ctop;
38338             //Roo.log("set scrolltop to ctop DISABLE?");
38339         }else if(cbot > sbot){
38340             //Roo.log("set scrolltop to cbot-ch");
38341             c.scrollTop = cbot-ch;
38342         }
38343         
38344         if(hscroll !== false){
38345             if(cleft < sleft){
38346                 c.scrollLeft = cleft;
38347             }else if(cright > sright){
38348                 c.scrollLeft = cright-c.clientWidth;
38349             }
38350         }
38351          
38352         return el;
38353     },
38354
38355     updateColumns : function(){
38356         this.grid.stopEditing();
38357         var cm = this.grid.colModel, colIds = this.getColumnIds();
38358         //var totalWidth = cm.getTotalWidth();
38359         var pos = 0;
38360         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38361             //if(cm.isHidden(i)) continue;
38362             var w = cm.getColumnWidth(i);
38363             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38364             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38365         }
38366         this.updateSplitters();
38367     },
38368
38369     generateRules : function(cm){
38370         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38371         Roo.util.CSS.removeStyleSheet(rulesId);
38372         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38373             var cid = cm.getColumnId(i);
38374             var align = '';
38375             if(cm.config[i].align){
38376                 align = 'text-align:'+cm.config[i].align+';';
38377             }
38378             var hidden = '';
38379             if(cm.isHidden(i)){
38380                 hidden = 'display:none;';
38381             }
38382             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38383             ruleBuf.push(
38384                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38385                     this.hdSelector, cid, " {\n", align, width, "}\n",
38386                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38387                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38388         }
38389         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38390     },
38391
38392     updateSplitters : function(){
38393         var cm = this.cm, s = this.getSplitters();
38394         if(s){ // splitters not created yet
38395             var pos = 0, locked = true;
38396             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38397                 if(cm.isHidden(i)) {
38398                     continue;
38399                 }
38400                 var w = cm.getColumnWidth(i); // make sure it's a number
38401                 if(!cm.isLocked(i) && locked){
38402                     pos = 0;
38403                     locked = false;
38404                 }
38405                 pos += w;
38406                 s[i].style.left = (pos-this.splitOffset) + "px";
38407             }
38408         }
38409     },
38410
38411     handleHiddenChange : function(colModel, colIndex, hidden){
38412         if(hidden){
38413             this.hideColumn(colIndex);
38414         }else{
38415             this.unhideColumn(colIndex);
38416         }
38417     },
38418
38419     hideColumn : function(colIndex){
38420         var cid = this.getColumnId(colIndex);
38421         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38422         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38423         if(Roo.isSafari){
38424             this.updateHeaders();
38425         }
38426         this.updateSplitters();
38427         this.layout();
38428     },
38429
38430     unhideColumn : function(colIndex){
38431         var cid = this.getColumnId(colIndex);
38432         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38433         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38434
38435         if(Roo.isSafari){
38436             this.updateHeaders();
38437         }
38438         this.updateSplitters();
38439         this.layout();
38440     },
38441
38442     insertRows : function(dm, firstRow, lastRow, isUpdate){
38443         if(firstRow == 0 && lastRow == dm.getCount()-1){
38444             this.refresh();
38445         }else{
38446             if(!isUpdate){
38447                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38448             }
38449             var s = this.getScrollState();
38450             var markup = this.renderRows(firstRow, lastRow);
38451             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38452             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38453             this.restoreScroll(s);
38454             if(!isUpdate){
38455                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38456                 this.syncRowHeights(firstRow, lastRow);
38457                 this.stripeRows(firstRow);
38458                 this.layout();
38459             }
38460         }
38461     },
38462
38463     bufferRows : function(markup, target, index){
38464         var before = null, trows = target.rows, tbody = target.tBodies[0];
38465         if(index < trows.length){
38466             before = trows[index];
38467         }
38468         var b = document.createElement("div");
38469         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38470         var rows = b.firstChild.rows;
38471         for(var i = 0, len = rows.length; i < len; i++){
38472             if(before){
38473                 tbody.insertBefore(rows[0], before);
38474             }else{
38475                 tbody.appendChild(rows[0]);
38476             }
38477         }
38478         b.innerHTML = "";
38479         b = null;
38480     },
38481
38482     deleteRows : function(dm, firstRow, lastRow){
38483         if(dm.getRowCount()<1){
38484             this.fireEvent("beforerefresh", this);
38485             this.mainBody.update("");
38486             this.lockedBody.update("");
38487             this.fireEvent("refresh", this);
38488         }else{
38489             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38490             var bt = this.getBodyTable();
38491             var tbody = bt.firstChild;
38492             var rows = bt.rows;
38493             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38494                 tbody.removeChild(rows[firstRow]);
38495             }
38496             this.stripeRows(firstRow);
38497             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38498         }
38499     },
38500
38501     updateRows : function(dataSource, firstRow, lastRow){
38502         var s = this.getScrollState();
38503         this.refresh();
38504         this.restoreScroll(s);
38505     },
38506
38507     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38508         if(!noRefresh){
38509            this.refresh();
38510         }
38511         this.updateHeaderSortState();
38512     },
38513
38514     getScrollState : function(){
38515         
38516         var sb = this.scroller.dom;
38517         return {left: sb.scrollLeft, top: sb.scrollTop};
38518     },
38519
38520     stripeRows : function(startRow){
38521         if(!this.grid.stripeRows || this.ds.getCount() < 1){
38522             return;
38523         }
38524         startRow = startRow || 0;
38525         var rows = this.getBodyTable().rows;
38526         var lrows = this.getLockedTable().rows;
38527         var cls = ' x-grid-row-alt ';
38528         for(var i = startRow, len = rows.length; i < len; i++){
38529             var row = rows[i], lrow = lrows[i];
38530             var isAlt = ((i+1) % 2 == 0);
38531             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38532             if(isAlt == hasAlt){
38533                 continue;
38534             }
38535             if(isAlt){
38536                 row.className += " x-grid-row-alt";
38537             }else{
38538                 row.className = row.className.replace("x-grid-row-alt", "");
38539             }
38540             if(lrow){
38541                 lrow.className = row.className;
38542             }
38543         }
38544     },
38545
38546     restoreScroll : function(state){
38547         //Roo.log('GridView.restoreScroll');
38548         var sb = this.scroller.dom;
38549         sb.scrollLeft = state.left;
38550         sb.scrollTop = state.top;
38551         this.syncScroll();
38552     },
38553
38554     syncScroll : function(){
38555         //Roo.log('GridView.syncScroll');
38556         var sb = this.scroller.dom;
38557         var sh = this.mainHd.dom;
38558         var bs = this.mainBody.dom;
38559         var lv = this.lockedBody.dom;
38560         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38561         lv.scrollTop = bs.scrollTop = sb.scrollTop;
38562     },
38563
38564     handleScroll : function(e){
38565         this.syncScroll();
38566         var sb = this.scroller.dom;
38567         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38568         e.stopEvent();
38569     },
38570
38571     handleWheel : function(e){
38572         var d = e.getWheelDelta();
38573         this.scroller.dom.scrollTop -= d*22;
38574         // set this here to prevent jumpy scrolling on large tables
38575         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38576         e.stopEvent();
38577     },
38578
38579     renderRows : function(startRow, endRow){
38580         // pull in all the crap needed to render rows
38581         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38582         var colCount = cm.getColumnCount();
38583
38584         if(ds.getCount() < 1){
38585             return ["", ""];
38586         }
38587
38588         // build a map for all the columns
38589         var cs = [];
38590         for(var i = 0; i < colCount; i++){
38591             var name = cm.getDataIndex(i);
38592             cs[i] = {
38593                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38594                 renderer : cm.getRenderer(i),
38595                 id : cm.getColumnId(i),
38596                 locked : cm.isLocked(i),
38597                 has_editor : cm.isCellEditable(i)
38598             };
38599         }
38600
38601         startRow = startRow || 0;
38602         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38603
38604         // records to render
38605         var rs = ds.getRange(startRow, endRow);
38606
38607         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38608     },
38609
38610     // As much as I hate to duplicate code, this was branched because FireFox really hates
38611     // [].join("") on strings. The performance difference was substantial enough to
38612     // branch this function
38613     doRender : Roo.isGecko ?
38614             function(cs, rs, ds, startRow, colCount, stripe){
38615                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38616                 // buffers
38617                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38618                 
38619                 var hasListener = this.grid.hasListener('rowclass');
38620                 var rowcfg = {};
38621                 for(var j = 0, len = rs.length; j < len; j++){
38622                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38623                     for(var i = 0; i < colCount; i++){
38624                         c = cs[i];
38625                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38626                         p.id = c.id;
38627                         p.css = p.attr = "";
38628                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38629                         if(p.value == undefined || p.value === "") {
38630                             p.value = "&#160;";
38631                         }
38632                         if(c.has_editor){
38633                             p.css += ' x-grid-editable-cell';
38634                         }
38635                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38636                             p.css +=  ' x-grid-dirty-cell';
38637                         }
38638                         var markup = ct.apply(p);
38639                         if(!c.locked){
38640                             cb+= markup;
38641                         }else{
38642                             lcb+= markup;
38643                         }
38644                     }
38645                     var alt = [];
38646                     if(stripe && ((rowIndex+1) % 2 == 0)){
38647                         alt.push("x-grid-row-alt")
38648                     }
38649                     if(r.dirty){
38650                         alt.push(  " x-grid-dirty-row");
38651                     }
38652                     rp.cells = lcb;
38653                     if(this.getRowClass){
38654                         alt.push(this.getRowClass(r, rowIndex));
38655                     }
38656                     if (hasListener) {
38657                         rowcfg = {
38658                              
38659                             record: r,
38660                             rowIndex : rowIndex,
38661                             rowClass : ''
38662                         };
38663                         this.grid.fireEvent('rowclass', this, rowcfg);
38664                         alt.push(rowcfg.rowClass);
38665                     }
38666                     rp.alt = alt.join(" ");
38667                     lbuf+= rt.apply(rp);
38668                     rp.cells = cb;
38669                     buf+=  rt.apply(rp);
38670                 }
38671                 return [lbuf, buf];
38672             } :
38673             function(cs, rs, ds, startRow, colCount, stripe){
38674                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38675                 // buffers
38676                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38677                 var hasListener = this.grid.hasListener('rowclass');
38678  
38679                 var rowcfg = {};
38680                 for(var j = 0, len = rs.length; j < len; j++){
38681                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38682                     for(var i = 0; i < colCount; i++){
38683                         c = cs[i];
38684                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38685                         p.id = c.id;
38686                         p.css = p.attr = "";
38687                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38688                         if(p.value == undefined || p.value === "") {
38689                             p.value = "&#160;";
38690                         }
38691                         //Roo.log(c);
38692                          if(c.has_editor){
38693                             p.css += ' x-grid-editable-cell';
38694                         }
38695                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38696                             p.css += ' x-grid-dirty-cell' 
38697                         }
38698                         
38699                         var markup = ct.apply(p);
38700                         if(!c.locked){
38701                             cb[cb.length] = markup;
38702                         }else{
38703                             lcb[lcb.length] = markup;
38704                         }
38705                     }
38706                     var alt = [];
38707                     if(stripe && ((rowIndex+1) % 2 == 0)){
38708                         alt.push( "x-grid-row-alt");
38709                     }
38710                     if(r.dirty){
38711                         alt.push(" x-grid-dirty-row");
38712                     }
38713                     rp.cells = lcb;
38714                     if(this.getRowClass){
38715                         alt.push( this.getRowClass(r, rowIndex));
38716                     }
38717                     if (hasListener) {
38718                         rowcfg = {
38719                              
38720                             record: r,
38721                             rowIndex : rowIndex,
38722                             rowClass : ''
38723                         };
38724                         this.grid.fireEvent('rowclass', this, rowcfg);
38725                         alt.push(rowcfg.rowClass);
38726                     }
38727                     
38728                     rp.alt = alt.join(" ");
38729                     rp.cells = lcb.join("");
38730                     lbuf[lbuf.length] = rt.apply(rp);
38731                     rp.cells = cb.join("");
38732                     buf[buf.length] =  rt.apply(rp);
38733                 }
38734                 return [lbuf.join(""), buf.join("")];
38735             },
38736
38737     renderBody : function(){
38738         var markup = this.renderRows();
38739         var bt = this.templates.body;
38740         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38741     },
38742
38743     /**
38744      * Refreshes the grid
38745      * @param {Boolean} headersToo
38746      */
38747     refresh : function(headersToo){
38748         this.fireEvent("beforerefresh", this);
38749         this.grid.stopEditing();
38750         var result = this.renderBody();
38751         this.lockedBody.update(result[0]);
38752         this.mainBody.update(result[1]);
38753         if(headersToo === true){
38754             this.updateHeaders();
38755             this.updateColumns();
38756             this.updateSplitters();
38757             this.updateHeaderSortState();
38758         }
38759         this.syncRowHeights();
38760         this.layout();
38761         this.fireEvent("refresh", this);
38762     },
38763
38764     handleColumnMove : function(cm, oldIndex, newIndex){
38765         this.indexMap = null;
38766         var s = this.getScrollState();
38767         this.refresh(true);
38768         this.restoreScroll(s);
38769         this.afterMove(newIndex);
38770     },
38771
38772     afterMove : function(colIndex){
38773         if(this.enableMoveAnim && Roo.enableFx){
38774             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38775         }
38776         // if multisort - fix sortOrder, and reload..
38777         if (this.grid.dataSource.multiSort) {
38778             // the we can call sort again..
38779             var dm = this.grid.dataSource;
38780             var cm = this.grid.colModel;
38781             var so = [];
38782             for(var i = 0; i < cm.config.length; i++ ) {
38783                 
38784                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38785                     continue; // dont' bother, it's not in sort list or being set.
38786                 }
38787                 
38788                 so.push(cm.config[i].dataIndex);
38789             };
38790             dm.sortOrder = so;
38791             dm.load(dm.lastOptions);
38792             
38793             
38794         }
38795         
38796     },
38797
38798     updateCell : function(dm, rowIndex, dataIndex){
38799         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38800         if(typeof colIndex == "undefined"){ // not present in grid
38801             return;
38802         }
38803         var cm = this.grid.colModel;
38804         var cell = this.getCell(rowIndex, colIndex);
38805         var cellText = this.getCellText(rowIndex, colIndex);
38806
38807         var p = {
38808             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38809             id : cm.getColumnId(colIndex),
38810             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38811         };
38812         var renderer = cm.getRenderer(colIndex);
38813         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38814         if(typeof val == "undefined" || val === "") {
38815             val = "&#160;";
38816         }
38817         cellText.innerHTML = val;
38818         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38819         this.syncRowHeights(rowIndex, rowIndex);
38820     },
38821
38822     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38823         var maxWidth = 0;
38824         if(this.grid.autoSizeHeaders){
38825             var h = this.getHeaderCellMeasure(colIndex);
38826             maxWidth = Math.max(maxWidth, h.scrollWidth);
38827         }
38828         var tb, index;
38829         if(this.cm.isLocked(colIndex)){
38830             tb = this.getLockedTable();
38831             index = colIndex;
38832         }else{
38833             tb = this.getBodyTable();
38834             index = colIndex - this.cm.getLockedCount();
38835         }
38836         if(tb && tb.rows){
38837             var rows = tb.rows;
38838             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38839             for(var i = 0; i < stopIndex; i++){
38840                 var cell = rows[i].childNodes[index].firstChild;
38841                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38842             }
38843         }
38844         return maxWidth + /*margin for error in IE*/ 5;
38845     },
38846     /**
38847      * Autofit a column to its content.
38848      * @param {Number} colIndex
38849      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38850      */
38851      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38852          if(this.cm.isHidden(colIndex)){
38853              return; // can't calc a hidden column
38854          }
38855         if(forceMinSize){
38856             var cid = this.cm.getColumnId(colIndex);
38857             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38858            if(this.grid.autoSizeHeaders){
38859                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38860            }
38861         }
38862         var newWidth = this.calcColumnWidth(colIndex);
38863         this.cm.setColumnWidth(colIndex,
38864             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38865         if(!suppressEvent){
38866             this.grid.fireEvent("columnresize", colIndex, newWidth);
38867         }
38868     },
38869
38870     /**
38871      * Autofits all columns to their content and then expands to fit any extra space in the grid
38872      */
38873      autoSizeColumns : function(){
38874         var cm = this.grid.colModel;
38875         var colCount = cm.getColumnCount();
38876         for(var i = 0; i < colCount; i++){
38877             this.autoSizeColumn(i, true, true);
38878         }
38879         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38880             this.fitColumns();
38881         }else{
38882             this.updateColumns();
38883             this.layout();
38884         }
38885     },
38886
38887     /**
38888      * Autofits all columns to the grid's width proportionate with their current size
38889      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38890      */
38891     fitColumns : function(reserveScrollSpace){
38892         var cm = this.grid.colModel;
38893         var colCount = cm.getColumnCount();
38894         var cols = [];
38895         var width = 0;
38896         var i, w;
38897         for (i = 0; i < colCount; i++){
38898             if(!cm.isHidden(i) && !cm.isFixed(i)){
38899                 w = cm.getColumnWidth(i);
38900                 cols.push(i);
38901                 cols.push(w);
38902                 width += w;
38903             }
38904         }
38905         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38906         if(reserveScrollSpace){
38907             avail -= 17;
38908         }
38909         var frac = (avail - cm.getTotalWidth())/width;
38910         while (cols.length){
38911             w = cols.pop();
38912             i = cols.pop();
38913             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38914         }
38915         this.updateColumns();
38916         this.layout();
38917     },
38918
38919     onRowSelect : function(rowIndex){
38920         var row = this.getRowComposite(rowIndex);
38921         row.addClass("x-grid-row-selected");
38922     },
38923
38924     onRowDeselect : function(rowIndex){
38925         var row = this.getRowComposite(rowIndex);
38926         row.removeClass("x-grid-row-selected");
38927     },
38928
38929     onCellSelect : function(row, col){
38930         var cell = this.getCell(row, col);
38931         if(cell){
38932             Roo.fly(cell).addClass("x-grid-cell-selected");
38933         }
38934     },
38935
38936     onCellDeselect : function(row, col){
38937         var cell = this.getCell(row, col);
38938         if(cell){
38939             Roo.fly(cell).removeClass("x-grid-cell-selected");
38940         }
38941     },
38942
38943     updateHeaderSortState : function(){
38944         
38945         // sort state can be single { field: xxx, direction : yyy}
38946         // or   { xxx=>ASC , yyy : DESC ..... }
38947         
38948         var mstate = {};
38949         if (!this.ds.multiSort) { 
38950             var state = this.ds.getSortState();
38951             if(!state){
38952                 return;
38953             }
38954             mstate[state.field] = state.direction;
38955             // FIXME... - this is not used here.. but might be elsewhere..
38956             this.sortState = state;
38957             
38958         } else {
38959             mstate = this.ds.sortToggle;
38960         }
38961         //remove existing sort classes..
38962         
38963         var sc = this.sortClasses;
38964         var hds = this.el.select(this.headerSelector).removeClass(sc);
38965         
38966         for(var f in mstate) {
38967         
38968             var sortColumn = this.cm.findColumnIndex(f);
38969             
38970             if(sortColumn != -1){
38971                 var sortDir = mstate[f];        
38972                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38973             }
38974         }
38975         
38976          
38977         
38978     },
38979
38980
38981     handleHeaderClick : function(g, index,e){
38982         
38983         Roo.log("header click");
38984         
38985         if (Roo.isTouch) {
38986             // touch events on header are handled by context
38987             this.handleHdCtx(g,index,e);
38988             return;
38989         }
38990         
38991         
38992         if(this.headersDisabled){
38993             return;
38994         }
38995         var dm = g.dataSource, cm = g.colModel;
38996         if(!cm.isSortable(index)){
38997             return;
38998         }
38999         g.stopEditing();
39000         
39001         if (dm.multiSort) {
39002             // update the sortOrder
39003             var so = [];
39004             for(var i = 0; i < cm.config.length; i++ ) {
39005                 
39006                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
39007                     continue; // dont' bother, it's not in sort list or being set.
39008                 }
39009                 
39010                 so.push(cm.config[i].dataIndex);
39011             };
39012             dm.sortOrder = so;
39013         }
39014         
39015         
39016         dm.sort(cm.getDataIndex(index));
39017     },
39018
39019
39020     destroy : function(){
39021         if(this.colMenu){
39022             this.colMenu.removeAll();
39023             Roo.menu.MenuMgr.unregister(this.colMenu);
39024             this.colMenu.getEl().remove();
39025             delete this.colMenu;
39026         }
39027         if(this.hmenu){
39028             this.hmenu.removeAll();
39029             Roo.menu.MenuMgr.unregister(this.hmenu);
39030             this.hmenu.getEl().remove();
39031             delete this.hmenu;
39032         }
39033         if(this.grid.enableColumnMove){
39034             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39035             if(dds){
39036                 for(var dd in dds){
39037                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
39038                         var elid = dds[dd].dragElId;
39039                         dds[dd].unreg();
39040                         Roo.get(elid).remove();
39041                     } else if(dds[dd].config.isTarget){
39042                         dds[dd].proxyTop.remove();
39043                         dds[dd].proxyBottom.remove();
39044                         dds[dd].unreg();
39045                     }
39046                     if(Roo.dd.DDM.locationCache[dd]){
39047                         delete Roo.dd.DDM.locationCache[dd];
39048                     }
39049                 }
39050                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39051             }
39052         }
39053         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39054         this.bind(null, null);
39055         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39056     },
39057
39058     handleLockChange : function(){
39059         this.refresh(true);
39060     },
39061
39062     onDenyColumnLock : function(){
39063
39064     },
39065
39066     onDenyColumnHide : function(){
39067
39068     },
39069
39070     handleHdMenuClick : function(item){
39071         var index = this.hdCtxIndex;
39072         var cm = this.cm, ds = this.ds;
39073         switch(item.id){
39074             case "asc":
39075                 ds.sort(cm.getDataIndex(index), "ASC");
39076                 break;
39077             case "desc":
39078                 ds.sort(cm.getDataIndex(index), "DESC");
39079                 break;
39080             case "lock":
39081                 var lc = cm.getLockedCount();
39082                 if(cm.getColumnCount(true) <= lc+1){
39083                     this.onDenyColumnLock();
39084                     return;
39085                 }
39086                 if(lc != index){
39087                     cm.setLocked(index, true, true);
39088                     cm.moveColumn(index, lc);
39089                     this.grid.fireEvent("columnmove", index, lc);
39090                 }else{
39091                     cm.setLocked(index, true);
39092                 }
39093             break;
39094             case "unlock":
39095                 var lc = cm.getLockedCount();
39096                 if((lc-1) != index){
39097                     cm.setLocked(index, false, true);
39098                     cm.moveColumn(index, lc-1);
39099                     this.grid.fireEvent("columnmove", index, lc-1);
39100                 }else{
39101                     cm.setLocked(index, false);
39102                 }
39103             break;
39104             case 'wider': // used to expand cols on touch..
39105             case 'narrow':
39106                 var cw = cm.getColumnWidth(index);
39107                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39108                 cw = Math.max(0, cw);
39109                 cw = Math.min(cw,4000);
39110                 cm.setColumnWidth(index, cw);
39111                 break;
39112                 
39113             default:
39114                 index = cm.getIndexById(item.id.substr(4));
39115                 if(index != -1){
39116                     if(item.checked && cm.getColumnCount(true) <= 1){
39117                         this.onDenyColumnHide();
39118                         return false;
39119                     }
39120                     cm.setHidden(index, item.checked);
39121                 }
39122         }
39123         return true;
39124     },
39125
39126     beforeColMenuShow : function(){
39127         var cm = this.cm,  colCount = cm.getColumnCount();
39128         this.colMenu.removeAll();
39129         
39130         var items = [];
39131         for(var i = 0; i < colCount; i++){
39132             items.push({
39133                 id: "col-"+cm.getColumnId(i),
39134                 text: cm.getColumnHeader(i),
39135                 checked: !cm.isHidden(i),
39136                 hideOnClick:false
39137             });
39138         }
39139         
39140         if (this.grid.sortColMenu) {
39141             items.sort(function(a,b) {
39142                 if (a.text == b.text) {
39143                     return 0;
39144                 }
39145                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39146             });
39147         }
39148         
39149         for(var i = 0; i < colCount; i++){
39150             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39151         }
39152     },
39153
39154     handleHdCtx : function(g, index, e){
39155         e.stopEvent();
39156         var hd = this.getHeaderCell(index);
39157         this.hdCtxIndex = index;
39158         var ms = this.hmenu.items, cm = this.cm;
39159         ms.get("asc").setDisabled(!cm.isSortable(index));
39160         ms.get("desc").setDisabled(!cm.isSortable(index));
39161         if(this.grid.enableColLock !== false){
39162             ms.get("lock").setDisabled(cm.isLocked(index));
39163             ms.get("unlock").setDisabled(!cm.isLocked(index));
39164         }
39165         this.hmenu.show(hd, "tl-bl");
39166     },
39167
39168     handleHdOver : function(e){
39169         var hd = this.findHeaderCell(e.getTarget());
39170         if(hd && !this.headersDisabled){
39171             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39172                this.fly(hd).addClass("x-grid-hd-over");
39173             }
39174         }
39175     },
39176
39177     handleHdOut : function(e){
39178         var hd = this.findHeaderCell(e.getTarget());
39179         if(hd){
39180             this.fly(hd).removeClass("x-grid-hd-over");
39181         }
39182     },
39183
39184     handleSplitDblClick : function(e, t){
39185         var i = this.getCellIndex(t);
39186         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39187             this.autoSizeColumn(i, true);
39188             this.layout();
39189         }
39190     },
39191
39192     render : function(){
39193
39194         var cm = this.cm;
39195         var colCount = cm.getColumnCount();
39196
39197         if(this.grid.monitorWindowResize === true){
39198             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39199         }
39200         var header = this.renderHeaders();
39201         var body = this.templates.body.apply({rows:""});
39202         var html = this.templates.master.apply({
39203             lockedBody: body,
39204             body: body,
39205             lockedHeader: header[0],
39206             header: header[1]
39207         });
39208
39209         //this.updateColumns();
39210
39211         this.grid.getGridEl().dom.innerHTML = html;
39212
39213         this.initElements();
39214         
39215         // a kludge to fix the random scolling effect in webkit
39216         this.el.on("scroll", function() {
39217             this.el.dom.scrollTop=0; // hopefully not recursive..
39218         },this);
39219
39220         this.scroller.on("scroll", this.handleScroll, this);
39221         this.lockedBody.on("mousewheel", this.handleWheel, this);
39222         this.mainBody.on("mousewheel", this.handleWheel, this);
39223
39224         this.mainHd.on("mouseover", this.handleHdOver, this);
39225         this.mainHd.on("mouseout", this.handleHdOut, this);
39226         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39227                 {delegate: "."+this.splitClass});
39228
39229         this.lockedHd.on("mouseover", this.handleHdOver, this);
39230         this.lockedHd.on("mouseout", this.handleHdOut, this);
39231         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39232                 {delegate: "."+this.splitClass});
39233
39234         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39235             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39236         }
39237
39238         this.updateSplitters();
39239
39240         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39241             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39242             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39243         }
39244
39245         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39246             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39247             this.hmenu.add(
39248                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39249                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39250             );
39251             if(this.grid.enableColLock !== false){
39252                 this.hmenu.add('-',
39253                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39254                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39255                 );
39256             }
39257             if (Roo.isTouch) {
39258                  this.hmenu.add('-',
39259                     {id:"wider", text: this.columnsWiderText},
39260                     {id:"narrow", text: this.columnsNarrowText }
39261                 );
39262                 
39263                  
39264             }
39265             
39266             if(this.grid.enableColumnHide !== false){
39267
39268                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39269                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39270                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39271
39272                 this.hmenu.add('-',
39273                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39274                 );
39275             }
39276             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39277
39278             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39279         }
39280
39281         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39282             this.dd = new Roo.grid.GridDragZone(this.grid, {
39283                 ddGroup : this.grid.ddGroup || 'GridDD'
39284             });
39285             
39286         }
39287
39288         /*
39289         for(var i = 0; i < colCount; i++){
39290             if(cm.isHidden(i)){
39291                 this.hideColumn(i);
39292             }
39293             if(cm.config[i].align){
39294                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39295                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39296             }
39297         }*/
39298         
39299         this.updateHeaderSortState();
39300
39301         this.beforeInitialResize();
39302         this.layout(true);
39303
39304         // two part rendering gives faster view to the user
39305         this.renderPhase2.defer(1, this);
39306     },
39307
39308     renderPhase2 : function(){
39309         // render the rows now
39310         this.refresh();
39311         if(this.grid.autoSizeColumns){
39312             this.autoSizeColumns();
39313         }
39314     },
39315
39316     beforeInitialResize : function(){
39317
39318     },
39319
39320     onColumnSplitterMoved : function(i, w){
39321         this.userResized = true;
39322         var cm = this.grid.colModel;
39323         cm.setColumnWidth(i, w, true);
39324         var cid = cm.getColumnId(i);
39325         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39326         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39327         this.updateSplitters();
39328         this.layout();
39329         this.grid.fireEvent("columnresize", i, w);
39330     },
39331
39332     syncRowHeights : function(startIndex, endIndex){
39333         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39334             startIndex = startIndex || 0;
39335             var mrows = this.getBodyTable().rows;
39336             var lrows = this.getLockedTable().rows;
39337             var len = mrows.length-1;
39338             endIndex = Math.min(endIndex || len, len);
39339             for(var i = startIndex; i <= endIndex; i++){
39340                 var m = mrows[i], l = lrows[i];
39341                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39342                 m.style.height = l.style.height = h + "px";
39343             }
39344         }
39345     },
39346
39347     layout : function(initialRender, is2ndPass)
39348     {
39349         var g = this.grid;
39350         var auto = g.autoHeight;
39351         var scrollOffset = 16;
39352         var c = g.getGridEl(), cm = this.cm,
39353                 expandCol = g.autoExpandColumn,
39354                 gv = this;
39355         //c.beginMeasure();
39356
39357         if(!c.dom.offsetWidth){ // display:none?
39358             if(initialRender){
39359                 this.lockedWrap.show();
39360                 this.mainWrap.show();
39361             }
39362             return;
39363         }
39364
39365         var hasLock = this.cm.isLocked(0);
39366
39367         var tbh = this.headerPanel.getHeight();
39368         var bbh = this.footerPanel.getHeight();
39369
39370         if(auto){
39371             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39372             var newHeight = ch + c.getBorderWidth("tb");
39373             if(g.maxHeight){
39374                 newHeight = Math.min(g.maxHeight, newHeight);
39375             }
39376             c.setHeight(newHeight);
39377         }
39378
39379         if(g.autoWidth){
39380             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39381         }
39382
39383         var s = this.scroller;
39384
39385         var csize = c.getSize(true);
39386
39387         this.el.setSize(csize.width, csize.height);
39388
39389         this.headerPanel.setWidth(csize.width);
39390         this.footerPanel.setWidth(csize.width);
39391
39392         var hdHeight = this.mainHd.getHeight();
39393         var vw = csize.width;
39394         var vh = csize.height - (tbh + bbh);
39395
39396         s.setSize(vw, vh);
39397
39398         var bt = this.getBodyTable();
39399         
39400         if(cm.getLockedCount() == cm.config.length){
39401             bt = this.getLockedTable();
39402         }
39403         
39404         var ltWidth = hasLock ?
39405                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39406
39407         var scrollHeight = bt.offsetHeight;
39408         var scrollWidth = ltWidth + bt.offsetWidth;
39409         var vscroll = false, hscroll = false;
39410
39411         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39412
39413         var lw = this.lockedWrap, mw = this.mainWrap;
39414         var lb = this.lockedBody, mb = this.mainBody;
39415
39416         setTimeout(function(){
39417             var t = s.dom.offsetTop;
39418             var w = s.dom.clientWidth,
39419                 h = s.dom.clientHeight;
39420
39421             lw.setTop(t);
39422             lw.setSize(ltWidth, h);
39423
39424             mw.setLeftTop(ltWidth, t);
39425             mw.setSize(w-ltWidth, h);
39426
39427             lb.setHeight(h-hdHeight);
39428             mb.setHeight(h-hdHeight);
39429
39430             if(is2ndPass !== true && !gv.userResized && expandCol){
39431                 // high speed resize without full column calculation
39432                 
39433                 var ci = cm.getIndexById(expandCol);
39434                 if (ci < 0) {
39435                     ci = cm.findColumnIndex(expandCol);
39436                 }
39437                 ci = Math.max(0, ci); // make sure it's got at least the first col.
39438                 var expandId = cm.getColumnId(ci);
39439                 var  tw = cm.getTotalWidth(false);
39440                 var currentWidth = cm.getColumnWidth(ci);
39441                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39442                 if(currentWidth != cw){
39443                     cm.setColumnWidth(ci, cw, true);
39444                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39445                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39446                     gv.updateSplitters();
39447                     gv.layout(false, true);
39448                 }
39449             }
39450
39451             if(initialRender){
39452                 lw.show();
39453                 mw.show();
39454             }
39455             //c.endMeasure();
39456         }, 10);
39457     },
39458
39459     onWindowResize : function(){
39460         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39461             return;
39462         }
39463         this.layout();
39464     },
39465
39466     appendFooter : function(parentEl){
39467         return null;
39468     },
39469
39470     sortAscText : "Sort Ascending",
39471     sortDescText : "Sort Descending",
39472     lockText : "Lock Column",
39473     unlockText : "Unlock Column",
39474     columnsText : "Columns",
39475  
39476     columnsWiderText : "Wider",
39477     columnsNarrowText : "Thinner"
39478 });
39479
39480
39481 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39482     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39483     this.proxy.el.addClass('x-grid3-col-dd');
39484 };
39485
39486 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39487     handleMouseDown : function(e){
39488
39489     },
39490
39491     callHandleMouseDown : function(e){
39492         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39493     }
39494 });
39495 /*
39496  * Based on:
39497  * Ext JS Library 1.1.1
39498  * Copyright(c) 2006-2007, Ext JS, LLC.
39499  *
39500  * Originally Released Under LGPL - original licence link has changed is not relivant.
39501  *
39502  * Fork - LGPL
39503  * <script type="text/javascript">
39504  */
39505  /**
39506  * @extends Roo.dd.DDProxy
39507  * @class Roo.grid.SplitDragZone
39508  * Support for Column Header resizing
39509  * @constructor
39510  * @param {Object} config
39511  */
39512 // private
39513 // This is a support class used internally by the Grid components
39514 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39515     this.grid = grid;
39516     this.view = grid.getView();
39517     this.proxy = this.view.resizeProxy;
39518     Roo.grid.SplitDragZone.superclass.constructor.call(
39519         this,
39520         hd, // ID
39521         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39522         {  // CONFIG
39523             dragElId : Roo.id(this.proxy.dom),
39524             resizeFrame:false
39525         }
39526     );
39527     
39528     this.setHandleElId(Roo.id(hd));
39529     if (hd2 !== false) {
39530         this.setOuterHandleElId(Roo.id(hd2));
39531     }
39532     
39533     this.scroll = false;
39534 };
39535 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39536     fly: Roo.Element.fly,
39537
39538     b4StartDrag : function(x, y){
39539         this.view.headersDisabled = true;
39540         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39541                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39542         );
39543         this.proxy.setHeight(h);
39544         
39545         // for old system colWidth really stored the actual width?
39546         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39547         // which in reality did not work.. - it worked only for fixed sizes
39548         // for resizable we need to use actual sizes.
39549         var w = this.cm.getColumnWidth(this.cellIndex);
39550         if (!this.view.mainWrap) {
39551             // bootstrap.
39552             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39553         }
39554         
39555         
39556         
39557         // this was w-this.grid.minColumnWidth;
39558         // doesnt really make sense? - w = thie curren width or the rendered one?
39559         var minw = Math.max(w-this.grid.minColumnWidth, 0);
39560         this.resetConstraints();
39561         this.setXConstraint(minw, 1000);
39562         this.setYConstraint(0, 0);
39563         this.minX = x - minw;
39564         this.maxX = x + 1000;
39565         this.startPos = x;
39566         if (!this.view.mainWrap) { // this is Bootstrap code..
39567             this.getDragEl().style.display='block';
39568         }
39569         
39570         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39571     },
39572
39573
39574     handleMouseDown : function(e){
39575         ev = Roo.EventObject.setEvent(e);
39576         var t = this.fly(ev.getTarget());
39577         if(t.hasClass("x-grid-split")){
39578             this.cellIndex = this.view.getCellIndex(t.dom);
39579             this.split = t.dom;
39580             this.cm = this.grid.colModel;
39581             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39582                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39583             }
39584         }
39585     },
39586
39587     endDrag : function(e){
39588         this.view.headersDisabled = false;
39589         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39590         var diff = endX - this.startPos;
39591         // 
39592         var w = this.cm.getColumnWidth(this.cellIndex);
39593         if (!this.view.mainWrap) {
39594             w = 0;
39595         }
39596         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39597     },
39598
39599     autoOffset : function(){
39600         this.setDelta(0,0);
39601     }
39602 });/*
39603  * Based on:
39604  * Ext JS Library 1.1.1
39605  * Copyright(c) 2006-2007, Ext JS, LLC.
39606  *
39607  * Originally Released Under LGPL - original licence link has changed is not relivant.
39608  *
39609  * Fork - LGPL
39610  * <script type="text/javascript">
39611  */
39612  
39613 // private
39614 // This is a support class used internally by the Grid components
39615 Roo.grid.GridDragZone = function(grid, config){
39616     this.view = grid.getView();
39617     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39618     if(this.view.lockedBody){
39619         this.setHandleElId(Roo.id(this.view.mainBody.dom));
39620         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39621     }
39622     this.scroll = false;
39623     this.grid = grid;
39624     this.ddel = document.createElement('div');
39625     this.ddel.className = 'x-grid-dd-wrap';
39626 };
39627
39628 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39629     ddGroup : "GridDD",
39630
39631     getDragData : function(e){
39632         var t = Roo.lib.Event.getTarget(e);
39633         var rowIndex = this.view.findRowIndex(t);
39634         var sm = this.grid.selModel;
39635             
39636         //Roo.log(rowIndex);
39637         
39638         if (sm.getSelectedCell) {
39639             // cell selection..
39640             if (!sm.getSelectedCell()) {
39641                 return false;
39642             }
39643             if (rowIndex != sm.getSelectedCell()[0]) {
39644                 return false;
39645             }
39646         
39647         }
39648         if (sm.getSelections && sm.getSelections().length < 1) {
39649             return false;
39650         }
39651         
39652         
39653         // before it used to all dragging of unseleted... - now we dont do that.
39654         if(rowIndex !== false){
39655             
39656             // if editorgrid.. 
39657             
39658             
39659             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39660                
39661             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39662               //  
39663             //}
39664             if (e.hasModifier()){
39665                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39666             }
39667             
39668             Roo.log("getDragData");
39669             
39670             return {
39671                 grid: this.grid,
39672                 ddel: this.ddel,
39673                 rowIndex: rowIndex,
39674                 selections: sm.getSelections ? sm.getSelections() : (
39675                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39676             };
39677         }
39678         return false;
39679     },
39680     
39681     
39682     onInitDrag : function(e){
39683         var data = this.dragData;
39684         this.ddel.innerHTML = this.grid.getDragDropText();
39685         this.proxy.update(this.ddel);
39686         // fire start drag?
39687     },
39688
39689     afterRepair : function(){
39690         this.dragging = false;
39691     },
39692
39693     getRepairXY : function(e, data){
39694         return false;
39695     },
39696
39697     onEndDrag : function(data, e){
39698         // fire end drag?
39699     },
39700
39701     onValidDrop : function(dd, e, id){
39702         // fire drag drop?
39703         this.hideProxy();
39704     },
39705
39706     beforeInvalidDrop : function(e, id){
39707
39708     }
39709 });/*
39710  * Based on:
39711  * Ext JS Library 1.1.1
39712  * Copyright(c) 2006-2007, Ext JS, LLC.
39713  *
39714  * Originally Released Under LGPL - original licence link has changed is not relivant.
39715  *
39716  * Fork - LGPL
39717  * <script type="text/javascript">
39718  */
39719  
39720
39721 /**
39722  * @class Roo.grid.ColumnModel
39723  * @extends Roo.util.Observable
39724  * This is the default implementation of a ColumnModel used by the Grid. It defines
39725  * the columns in the grid.
39726  * <br>Usage:<br>
39727  <pre><code>
39728  var colModel = new Roo.grid.ColumnModel([
39729         {header: "Ticker", width: 60, sortable: true, locked: true},
39730         {header: "Company Name", width: 150, sortable: true},
39731         {header: "Market Cap.", width: 100, sortable: true},
39732         {header: "$ Sales", width: 100, sortable: true, renderer: money},
39733         {header: "Employees", width: 100, sortable: true, resizable: false}
39734  ]);
39735  </code></pre>
39736  * <p>
39737  
39738  * The config options listed for this class are options which may appear in each
39739  * individual column definition.
39740  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39741  * @constructor
39742  * @param {Object} config An Array of column config objects. See this class's
39743  * config objects for details.
39744 */
39745 Roo.grid.ColumnModel = function(config){
39746         /**
39747      * The config passed into the constructor
39748      */
39749     this.config = []; //config;
39750     this.lookup = {};
39751
39752     // if no id, create one
39753     // if the column does not have a dataIndex mapping,
39754     // map it to the order it is in the config
39755     for(var i = 0, len = config.length; i < len; i++){
39756         this.addColumn(config[i]);
39757         
39758     }
39759
39760     /**
39761      * The width of columns which have no width specified (defaults to 100)
39762      * @type Number
39763      */
39764     this.defaultWidth = 100;
39765
39766     /**
39767      * Default sortable of columns which have no sortable specified (defaults to false)
39768      * @type Boolean
39769      */
39770     this.defaultSortable = false;
39771
39772     this.addEvents({
39773         /**
39774              * @event widthchange
39775              * Fires when the width of a column changes.
39776              * @param {ColumnModel} this
39777              * @param {Number} columnIndex The column index
39778              * @param {Number} newWidth The new width
39779              */
39780             "widthchange": true,
39781         /**
39782              * @event headerchange
39783              * Fires when the text of a header changes.
39784              * @param {ColumnModel} this
39785              * @param {Number} columnIndex The column index
39786              * @param {Number} newText The new header text
39787              */
39788             "headerchange": true,
39789         /**
39790              * @event hiddenchange
39791              * Fires when a column is hidden or "unhidden".
39792              * @param {ColumnModel} this
39793              * @param {Number} columnIndex The column index
39794              * @param {Boolean} hidden true if hidden, false otherwise
39795              */
39796             "hiddenchange": true,
39797             /**
39798          * @event columnmoved
39799          * Fires when a column is moved.
39800          * @param {ColumnModel} this
39801          * @param {Number} oldIndex
39802          * @param {Number} newIndex
39803          */
39804         "columnmoved" : true,
39805         /**
39806          * @event columlockchange
39807          * Fires when a column's locked state is changed
39808          * @param {ColumnModel} this
39809          * @param {Number} colIndex
39810          * @param {Boolean} locked true if locked
39811          */
39812         "columnlockchange" : true
39813     });
39814     Roo.grid.ColumnModel.superclass.constructor.call(this);
39815 };
39816 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39817     /**
39818      * @cfg {String} header [required] The header text to display in the Grid view.
39819      */
39820         /**
39821      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39822      */
39823         /**
39824      * @cfg {String} smHeader Header at Bootsrap Small width
39825      */
39826         /**
39827      * @cfg {String} mdHeader Header at Bootsrap Medium width
39828      */
39829         /**
39830      * @cfg {String} lgHeader Header at Bootsrap Large width
39831      */
39832         /**
39833      * @cfg {String} xlHeader Header at Bootsrap extra Large width
39834      */
39835     /**
39836      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
39837      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39838      * specified, the column's index is used as an index into the Record's data Array.
39839      */
39840     /**
39841      * @cfg {Number} width  The initial width in pixels of the column. Using this
39842      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39843      */
39844     /**
39845      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
39846      * Defaults to the value of the {@link #defaultSortable} property.
39847      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39848      */
39849     /**
39850      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
39851      */
39852     /**
39853      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
39854      */
39855     /**
39856      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
39857      */
39858     /**
39859      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
39860      */
39861     /**
39862      * @cfg {Function} renderer A function used to generate HTML markup for a cell
39863      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39864      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
39865      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39866      */
39867        /**
39868      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
39869      */
39870     /**
39871      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
39872      */
39873     /**
39874      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
39875      */
39876     /**
39877      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
39878      */
39879     /**
39880      * @cfg {String} tooltip mouse over tooltip text
39881      */
39882     /**
39883      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
39884      */
39885     /**
39886      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
39887      */
39888     /**
39889      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
39890      */
39891     /**
39892      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
39893      */
39894         /**
39895      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
39896      */
39897     /**
39898      * Returns the id of the column at the specified index.
39899      * @param {Number} index The column index
39900      * @return {String} the id
39901      */
39902     getColumnId : function(index){
39903         return this.config[index].id;
39904     },
39905
39906     /**
39907      * Returns the column for a specified id.
39908      * @param {String} id The column id
39909      * @return {Object} the column
39910      */
39911     getColumnById : function(id){
39912         return this.lookup[id];
39913     },
39914
39915     
39916     /**
39917      * Returns the column Object for a specified dataIndex.
39918      * @param {String} dataIndex The column dataIndex
39919      * @return {Object|Boolean} the column or false if not found
39920      */
39921     getColumnByDataIndex: function(dataIndex){
39922         var index = this.findColumnIndex(dataIndex);
39923         return index > -1 ? this.config[index] : false;
39924     },
39925     
39926     /**
39927      * Returns the index for a specified column id.
39928      * @param {String} id The column id
39929      * @return {Number} the index, or -1 if not found
39930      */
39931     getIndexById : function(id){
39932         for(var i = 0, len = this.config.length; i < len; i++){
39933             if(this.config[i].id == id){
39934                 return i;
39935             }
39936         }
39937         return -1;
39938     },
39939     
39940     /**
39941      * Returns the index for a specified column dataIndex.
39942      * @param {String} dataIndex The column dataIndex
39943      * @return {Number} the index, or -1 if not found
39944      */
39945     
39946     findColumnIndex : function(dataIndex){
39947         for(var i = 0, len = this.config.length; i < len; i++){
39948             if(this.config[i].dataIndex == dataIndex){
39949                 return i;
39950             }
39951         }
39952         return -1;
39953     },
39954     
39955     
39956     moveColumn : function(oldIndex, newIndex){
39957         var c = this.config[oldIndex];
39958         this.config.splice(oldIndex, 1);
39959         this.config.splice(newIndex, 0, c);
39960         this.dataMap = null;
39961         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39962     },
39963
39964     isLocked : function(colIndex){
39965         return this.config[colIndex].locked === true;
39966     },
39967
39968     setLocked : function(colIndex, value, suppressEvent){
39969         if(this.isLocked(colIndex) == value){
39970             return;
39971         }
39972         this.config[colIndex].locked = value;
39973         if(!suppressEvent){
39974             this.fireEvent("columnlockchange", this, colIndex, value);
39975         }
39976     },
39977
39978     getTotalLockedWidth : function(){
39979         var totalWidth = 0;
39980         for(var i = 0; i < this.config.length; i++){
39981             if(this.isLocked(i) && !this.isHidden(i)){
39982                 this.totalWidth += this.getColumnWidth(i);
39983             }
39984         }
39985         return totalWidth;
39986     },
39987
39988     getLockedCount : function(){
39989         for(var i = 0, len = this.config.length; i < len; i++){
39990             if(!this.isLocked(i)){
39991                 return i;
39992             }
39993         }
39994         
39995         return this.config.length;
39996     },
39997
39998     /**
39999      * Returns the number of columns.
40000      * @return {Number}
40001      */
40002     getColumnCount : function(visibleOnly){
40003         if(visibleOnly === true){
40004             var c = 0;
40005             for(var i = 0, len = this.config.length; i < len; i++){
40006                 if(!this.isHidden(i)){
40007                     c++;
40008                 }
40009             }
40010             return c;
40011         }
40012         return this.config.length;
40013     },
40014
40015     /**
40016      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
40017      * @param {Function} fn
40018      * @param {Object} scope (optional)
40019      * @return {Array} result
40020      */
40021     getColumnsBy : function(fn, scope){
40022         var r = [];
40023         for(var i = 0, len = this.config.length; i < len; i++){
40024             var c = this.config[i];
40025             if(fn.call(scope||this, c, i) === true){
40026                 r[r.length] = c;
40027             }
40028         }
40029         return r;
40030     },
40031
40032     /**
40033      * Returns true if the specified column is sortable.
40034      * @param {Number} col The column index
40035      * @return {Boolean}
40036      */
40037     isSortable : function(col){
40038         if(typeof this.config[col].sortable == "undefined"){
40039             return this.defaultSortable;
40040         }
40041         return this.config[col].sortable;
40042     },
40043
40044     /**
40045      * Returns the rendering (formatting) function defined for the column.
40046      * @param {Number} col The column index.
40047      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40048      */
40049     getRenderer : function(col){
40050         if(!this.config[col].renderer){
40051             return Roo.grid.ColumnModel.defaultRenderer;
40052         }
40053         return this.config[col].renderer;
40054     },
40055
40056     /**
40057      * Sets the rendering (formatting) function for a column.
40058      * @param {Number} col The column index
40059      * @param {Function} fn The function to use to process the cell's raw data
40060      * to return HTML markup for the grid view. The render function is called with
40061      * the following parameters:<ul>
40062      * <li>Data value.</li>
40063      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40064      * <li>css A CSS style string to apply to the table cell.</li>
40065      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40066      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40067      * <li>Row index</li>
40068      * <li>Column index</li>
40069      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40070      */
40071     setRenderer : function(col, fn){
40072         this.config[col].renderer = fn;
40073     },
40074
40075     /**
40076      * Returns the width for the specified column.
40077      * @param {Number} col The column index
40078      * @param (optional) {String} gridSize bootstrap width size.
40079      * @return {Number}
40080      */
40081     getColumnWidth : function(col, gridSize)
40082         {
40083                 var cfg = this.config[col];
40084                 
40085                 if (typeof(gridSize) == 'undefined') {
40086                         return cfg.width * 1 || this.defaultWidth;
40087                 }
40088                 if (gridSize === false) { // if we set it..
40089                         return cfg.width || false;
40090                 }
40091                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40092                 
40093                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40094                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40095                                 continue;
40096                         }
40097                         return cfg[ sizes[i] ];
40098                 }
40099                 return 1;
40100                 
40101     },
40102
40103     /**
40104      * Sets the width for a column.
40105      * @param {Number} col The column index
40106      * @param {Number} width The new width
40107      */
40108     setColumnWidth : function(col, width, suppressEvent){
40109         this.config[col].width = width;
40110         this.totalWidth = null;
40111         if(!suppressEvent){
40112              this.fireEvent("widthchange", this, col, width);
40113         }
40114     },
40115
40116     /**
40117      * Returns the total width of all columns.
40118      * @param {Boolean} includeHidden True to include hidden column widths
40119      * @return {Number}
40120      */
40121     getTotalWidth : function(includeHidden){
40122         if(!this.totalWidth){
40123             this.totalWidth = 0;
40124             for(var i = 0, len = this.config.length; i < len; i++){
40125                 if(includeHidden || !this.isHidden(i)){
40126                     this.totalWidth += this.getColumnWidth(i);
40127                 }
40128             }
40129         }
40130         return this.totalWidth;
40131     },
40132
40133     /**
40134      * Returns the header for the specified column.
40135      * @param {Number} col The column index
40136      * @return {String}
40137      */
40138     getColumnHeader : function(col){
40139         return this.config[col].header;
40140     },
40141
40142     /**
40143      * Sets the header for a column.
40144      * @param {Number} col The column index
40145      * @param {String} header The new header
40146      */
40147     setColumnHeader : function(col, header){
40148         this.config[col].header = header;
40149         this.fireEvent("headerchange", this, col, header);
40150     },
40151
40152     /**
40153      * Returns the tooltip for the specified column.
40154      * @param {Number} col The column index
40155      * @return {String}
40156      */
40157     getColumnTooltip : function(col){
40158             return this.config[col].tooltip;
40159     },
40160     /**
40161      * Sets the tooltip for a column.
40162      * @param {Number} col The column index
40163      * @param {String} tooltip The new tooltip
40164      */
40165     setColumnTooltip : function(col, tooltip){
40166             this.config[col].tooltip = tooltip;
40167     },
40168
40169     /**
40170      * Returns the dataIndex for the specified column.
40171      * @param {Number} col The column index
40172      * @return {Number}
40173      */
40174     getDataIndex : function(col){
40175         return this.config[col].dataIndex;
40176     },
40177
40178     /**
40179      * Sets the dataIndex for a column.
40180      * @param {Number} col The column index
40181      * @param {Number} dataIndex The new dataIndex
40182      */
40183     setDataIndex : function(col, dataIndex){
40184         this.config[col].dataIndex = dataIndex;
40185     },
40186
40187     
40188     
40189     /**
40190      * Returns true if the cell is editable.
40191      * @param {Number} colIndex The column index
40192      * @param {Number} rowIndex The row index - this is nto actually used..?
40193      * @return {Boolean}
40194      */
40195     isCellEditable : function(colIndex, rowIndex){
40196         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40197     },
40198
40199     /**
40200      * Returns the editor defined for the cell/column.
40201      * return false or null to disable editing.
40202      * @param {Number} colIndex The column index
40203      * @param {Number} rowIndex The row index
40204      * @return {Object}
40205      */
40206     getCellEditor : function(colIndex, rowIndex){
40207         return this.config[colIndex].editor;
40208     },
40209
40210     /**
40211      * Sets if a column is editable.
40212      * @param {Number} col The column index
40213      * @param {Boolean} editable True if the column is editable
40214      */
40215     setEditable : function(col, editable){
40216         this.config[col].editable = editable;
40217     },
40218
40219
40220     /**
40221      * Returns true if the column is hidden.
40222      * @param {Number} colIndex The column index
40223      * @return {Boolean}
40224      */
40225     isHidden : function(colIndex){
40226         return this.config[colIndex].hidden;
40227     },
40228
40229
40230     /**
40231      * Returns true if the column width cannot be changed
40232      */
40233     isFixed : function(colIndex){
40234         return this.config[colIndex].fixed;
40235     },
40236
40237     /**
40238      * Returns true if the column can be resized
40239      * @return {Boolean}
40240      */
40241     isResizable : function(colIndex){
40242         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40243     },
40244     /**
40245      * Sets if a column is hidden.
40246      * @param {Number} colIndex The column index
40247      * @param {Boolean} hidden True if the column is hidden
40248      */
40249     setHidden : function(colIndex, hidden){
40250         this.config[colIndex].hidden = hidden;
40251         this.totalWidth = null;
40252         this.fireEvent("hiddenchange", this, colIndex, hidden);
40253     },
40254
40255     /**
40256      * Sets the editor for a column.
40257      * @param {Number} col The column index
40258      * @param {Object} editor The editor object
40259      */
40260     setEditor : function(col, editor){
40261         this.config[col].editor = editor;
40262     },
40263     /**
40264      * Add a column (experimental...) - defaults to adding to the end..
40265      * @param {Object} config 
40266     */
40267     addColumn : function(c)
40268     {
40269     
40270         var i = this.config.length;
40271         this.config[i] = c;
40272         
40273         if(typeof c.dataIndex == "undefined"){
40274             c.dataIndex = i;
40275         }
40276         if(typeof c.renderer == "string"){
40277             c.renderer = Roo.util.Format[c.renderer];
40278         }
40279         if(typeof c.id == "undefined"){
40280             c.id = Roo.id();
40281         }
40282         if(c.editor && c.editor.xtype){
40283             c.editor  = Roo.factory(c.editor, Roo.grid);
40284         }
40285         if(c.editor && c.editor.isFormField){
40286             c.editor = new Roo.grid.GridEditor(c.editor);
40287         }
40288         this.lookup[c.id] = c;
40289     }
40290     
40291 });
40292
40293 Roo.grid.ColumnModel.defaultRenderer = function(value)
40294 {
40295     if(typeof value == "object") {
40296         return value;
40297     }
40298         if(typeof value == "string" && value.length < 1){
40299             return "&#160;";
40300         }
40301     
40302         return String.format("{0}", value);
40303 };
40304
40305 // Alias for backwards compatibility
40306 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40307 /*
40308  * Based on:
40309  * Ext JS Library 1.1.1
40310  * Copyright(c) 2006-2007, Ext JS, LLC.
40311  *
40312  * Originally Released Under LGPL - original licence link has changed is not relivant.
40313  *
40314  * Fork - LGPL
40315  * <script type="text/javascript">
40316  */
40317
40318 /**
40319  * @class Roo.grid.AbstractSelectionModel
40320  * @extends Roo.util.Observable
40321  * @abstract
40322  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40323  * implemented by descendant classes.  This class should not be directly instantiated.
40324  * @constructor
40325  */
40326 Roo.grid.AbstractSelectionModel = function(){
40327     this.locked = false;
40328     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40329 };
40330
40331 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40332     /** @ignore Called by the grid automatically. Do not call directly. */
40333     init : function(grid){
40334         this.grid = grid;
40335         this.initEvents();
40336     },
40337
40338     /**
40339      * Locks the selections.
40340      */
40341     lock : function(){
40342         this.locked = true;
40343     },
40344
40345     /**
40346      * Unlocks the selections.
40347      */
40348     unlock : function(){
40349         this.locked = false;
40350     },
40351
40352     /**
40353      * Returns true if the selections are locked.
40354      * @return {Boolean}
40355      */
40356     isLocked : function(){
40357         return this.locked;
40358     }
40359 });/*
40360  * Based on:
40361  * Ext JS Library 1.1.1
40362  * Copyright(c) 2006-2007, Ext JS, LLC.
40363  *
40364  * Originally Released Under LGPL - original licence link has changed is not relivant.
40365  *
40366  * Fork - LGPL
40367  * <script type="text/javascript">
40368  */
40369 /**
40370  * @extends Roo.grid.AbstractSelectionModel
40371  * @class Roo.grid.RowSelectionModel
40372  * The default SelectionModel used by {@link Roo.grid.Grid}.
40373  * It supports multiple selections and keyboard selection/navigation. 
40374  * @constructor
40375  * @param {Object} config
40376  */
40377 Roo.grid.RowSelectionModel = function(config){
40378     Roo.apply(this, config);
40379     this.selections = new Roo.util.MixedCollection(false, function(o){
40380         return o.id;
40381     });
40382
40383     this.last = false;
40384     this.lastActive = false;
40385
40386     this.addEvents({
40387         /**
40388         * @event selectionchange
40389         * Fires when the selection changes
40390         * @param {SelectionModel} this
40391         */
40392        "selectionchange" : true,
40393        /**
40394         * @event afterselectionchange
40395         * Fires after the selection changes (eg. by key press or clicking)
40396         * @param {SelectionModel} this
40397         */
40398        "afterselectionchange" : true,
40399        /**
40400         * @event beforerowselect
40401         * Fires when a row is selected being selected, return false to cancel.
40402         * @param {SelectionModel} this
40403         * @param {Number} rowIndex The selected index
40404         * @param {Boolean} keepExisting False if other selections will be cleared
40405         */
40406        "beforerowselect" : true,
40407        /**
40408         * @event rowselect
40409         * Fires when a row is selected.
40410         * @param {SelectionModel} this
40411         * @param {Number} rowIndex The selected index
40412         * @param {Roo.data.Record} r The record
40413         */
40414        "rowselect" : true,
40415        /**
40416         * @event rowdeselect
40417         * Fires when a row is deselected.
40418         * @param {SelectionModel} this
40419         * @param {Number} rowIndex The selected index
40420         */
40421         "rowdeselect" : true
40422     });
40423     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40424     this.locked = false;
40425 };
40426
40427 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
40428     /**
40429      * @cfg {Boolean} singleSelect
40430      * True to allow selection of only one row at a time (defaults to false)
40431      */
40432     singleSelect : false,
40433
40434     // private
40435     initEvents : function(){
40436
40437         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40438             this.grid.on("mousedown", this.handleMouseDown, this);
40439         }else{ // allow click to work like normal
40440             this.grid.on("rowclick", this.handleDragableRowClick, this);
40441         }
40442         // bootstrap does not have a view..
40443         var view = this.grid.view ? this.grid.view : this.grid;
40444         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40445             "up" : function(e){
40446                 if(!e.shiftKey){
40447                     this.selectPrevious(e.shiftKey);
40448                 }else if(this.last !== false && this.lastActive !== false){
40449                     var last = this.last;
40450                     this.selectRange(this.last,  this.lastActive-1);
40451                     view.focusRow(this.lastActive);
40452                     if(last !== false){
40453                         this.last = last;
40454                     }
40455                 }else{
40456                     this.selectFirstRow();
40457                 }
40458                 this.fireEvent("afterselectionchange", this);
40459             },
40460             "down" : function(e){
40461                 if(!e.shiftKey){
40462                     this.selectNext(e.shiftKey);
40463                 }else if(this.last !== false && this.lastActive !== false){
40464                     var last = this.last;
40465                     this.selectRange(this.last,  this.lastActive+1);
40466                     view.focusRow(this.lastActive);
40467                     if(last !== false){
40468                         this.last = last;
40469                     }
40470                 }else{
40471                     this.selectFirstRow();
40472                 }
40473                 this.fireEvent("afterselectionchange", this);
40474             },
40475             scope: this
40476         });
40477
40478          
40479         view.on("refresh", this.onRefresh, this);
40480         view.on("rowupdated", this.onRowUpdated, this);
40481         view.on("rowremoved", this.onRemove, this);
40482     },
40483
40484     // private
40485     onRefresh : function(){
40486         var ds = this.grid.ds, i, v = this.grid.view;
40487         var s = this.selections;
40488         s.each(function(r){
40489             if((i = ds.indexOfId(r.id)) != -1){
40490                 v.onRowSelect(i);
40491                 s.add(ds.getAt(i)); // updating the selection relate data
40492             }else{
40493                 s.remove(r);
40494             }
40495         });
40496     },
40497
40498     // private
40499     onRemove : function(v, index, r){
40500         this.selections.remove(r);
40501     },
40502
40503     // private
40504     onRowUpdated : function(v, index, r){
40505         if(this.isSelected(r)){
40506             v.onRowSelect(index);
40507         }
40508     },
40509
40510     /**
40511      * Select records.
40512      * @param {Array} records The records to select
40513      * @param {Boolean} keepExisting (optional) True to keep existing selections
40514      */
40515     selectRecords : function(records, keepExisting){
40516         if(!keepExisting){
40517             this.clearSelections();
40518         }
40519         var ds = this.grid.ds;
40520         for(var i = 0, len = records.length; i < len; i++){
40521             this.selectRow(ds.indexOf(records[i]), true);
40522         }
40523     },
40524
40525     /**
40526      * Gets the number of selected rows.
40527      * @return {Number}
40528      */
40529     getCount : function(){
40530         return this.selections.length;
40531     },
40532
40533     /**
40534      * Selects the first row in the grid.
40535      */
40536     selectFirstRow : function(){
40537         this.selectRow(0);
40538     },
40539
40540     /**
40541      * Select the last row.
40542      * @param {Boolean} keepExisting (optional) True to keep existing selections
40543      */
40544     selectLastRow : function(keepExisting){
40545         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40546     },
40547
40548     /**
40549      * Selects the row immediately following the last selected row.
40550      * @param {Boolean} keepExisting (optional) True to keep existing selections
40551      */
40552     selectNext : function(keepExisting){
40553         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40554             this.selectRow(this.last+1, keepExisting);
40555             var view = this.grid.view ? this.grid.view : this.grid;
40556             view.focusRow(this.last);
40557         }
40558     },
40559
40560     /**
40561      * Selects the row that precedes the last selected row.
40562      * @param {Boolean} keepExisting (optional) True to keep existing selections
40563      */
40564     selectPrevious : function(keepExisting){
40565         if(this.last){
40566             this.selectRow(this.last-1, keepExisting);
40567             var view = this.grid.view ? this.grid.view : this.grid;
40568             view.focusRow(this.last);
40569         }
40570     },
40571
40572     /**
40573      * Returns the selected records
40574      * @return {Array} Array of selected records
40575      */
40576     getSelections : function(){
40577         return [].concat(this.selections.items);
40578     },
40579
40580     /**
40581      * Returns the first selected record.
40582      * @return {Record}
40583      */
40584     getSelected : function(){
40585         return this.selections.itemAt(0);
40586     },
40587
40588
40589     /**
40590      * Clears all selections.
40591      */
40592     clearSelections : function(fast){
40593         if(this.locked) {
40594             return;
40595         }
40596         if(fast !== true){
40597             var ds = this.grid.ds;
40598             var s = this.selections;
40599             s.each(function(r){
40600                 this.deselectRow(ds.indexOfId(r.id));
40601             }, this);
40602             s.clear();
40603         }else{
40604             this.selections.clear();
40605         }
40606         this.last = false;
40607     },
40608
40609
40610     /**
40611      * Selects all rows.
40612      */
40613     selectAll : function(){
40614         if(this.locked) {
40615             return;
40616         }
40617         this.selections.clear();
40618         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40619             this.selectRow(i, true);
40620         }
40621     },
40622
40623     /**
40624      * Returns True if there is a selection.
40625      * @return {Boolean}
40626      */
40627     hasSelection : function(){
40628         return this.selections.length > 0;
40629     },
40630
40631     /**
40632      * Returns True if the specified row is selected.
40633      * @param {Number/Record} record The record or index of the record to check
40634      * @return {Boolean}
40635      */
40636     isSelected : function(index){
40637         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40638         return (r && this.selections.key(r.id) ? true : false);
40639     },
40640
40641     /**
40642      * Returns True if the specified record id is selected.
40643      * @param {String} id The id of record to check
40644      * @return {Boolean}
40645      */
40646     isIdSelected : function(id){
40647         return (this.selections.key(id) ? true : false);
40648     },
40649
40650     // private
40651     handleMouseDown : function(e, t)
40652     {
40653         var view = this.grid.view ? this.grid.view : this.grid;
40654         var rowIndex;
40655         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40656             return;
40657         };
40658         if(e.shiftKey && this.last !== false){
40659             var last = this.last;
40660             this.selectRange(last, rowIndex, e.ctrlKey);
40661             this.last = last; // reset the last
40662             view.focusRow(rowIndex);
40663         }else{
40664             var isSelected = this.isSelected(rowIndex);
40665             if(e.button !== 0 && isSelected){
40666                 view.focusRow(rowIndex);
40667             }else if(e.ctrlKey && isSelected){
40668                 this.deselectRow(rowIndex);
40669             }else if(!isSelected){
40670                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40671                 view.focusRow(rowIndex);
40672             }
40673         }
40674         this.fireEvent("afterselectionchange", this);
40675     },
40676     // private
40677     handleDragableRowClick :  function(grid, rowIndex, e) 
40678     {
40679         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40680             this.selectRow(rowIndex, false);
40681             var view = this.grid.view ? this.grid.view : this.grid;
40682             view.focusRow(rowIndex);
40683              this.fireEvent("afterselectionchange", this);
40684         }
40685     },
40686     
40687     /**
40688      * Selects multiple rows.
40689      * @param {Array} rows Array of the indexes of the row to select
40690      * @param {Boolean} keepExisting (optional) True to keep existing selections
40691      */
40692     selectRows : function(rows, keepExisting){
40693         if(!keepExisting){
40694             this.clearSelections();
40695         }
40696         for(var i = 0, len = rows.length; i < len; i++){
40697             this.selectRow(rows[i], true);
40698         }
40699     },
40700
40701     /**
40702      * Selects a range of rows. All rows in between startRow and endRow are also selected.
40703      * @param {Number} startRow The index of the first row in the range
40704      * @param {Number} endRow The index of the last row in the range
40705      * @param {Boolean} keepExisting (optional) True to retain existing selections
40706      */
40707     selectRange : function(startRow, endRow, keepExisting){
40708         if(this.locked) {
40709             return;
40710         }
40711         if(!keepExisting){
40712             this.clearSelections();
40713         }
40714         if(startRow <= endRow){
40715             for(var i = startRow; i <= endRow; i++){
40716                 this.selectRow(i, true);
40717             }
40718         }else{
40719             for(var i = startRow; i >= endRow; i--){
40720                 this.selectRow(i, true);
40721             }
40722         }
40723     },
40724
40725     /**
40726      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40727      * @param {Number} startRow The index of the first row in the range
40728      * @param {Number} endRow The index of the last row in the range
40729      */
40730     deselectRange : function(startRow, endRow, preventViewNotify){
40731         if(this.locked) {
40732             return;
40733         }
40734         for(var i = startRow; i <= endRow; i++){
40735             this.deselectRow(i, preventViewNotify);
40736         }
40737     },
40738
40739     /**
40740      * Selects a row.
40741      * @param {Number} row The index of the row to select
40742      * @param {Boolean} keepExisting (optional) True to keep existing selections
40743      */
40744     selectRow : function(index, keepExisting, preventViewNotify){
40745         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40746             return;
40747         }
40748         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40749             if(!keepExisting || this.singleSelect){
40750                 this.clearSelections();
40751             }
40752             var r = this.grid.ds.getAt(index);
40753             this.selections.add(r);
40754             this.last = this.lastActive = index;
40755             if(!preventViewNotify){
40756                 var view = this.grid.view ? this.grid.view : this.grid;
40757                 view.onRowSelect(index);
40758             }
40759             this.fireEvent("rowselect", this, index, r);
40760             this.fireEvent("selectionchange", this);
40761         }
40762     },
40763
40764     /**
40765      * Deselects a row.
40766      * @param {Number} row The index of the row to deselect
40767      */
40768     deselectRow : function(index, preventViewNotify){
40769         if(this.locked) {
40770             return;
40771         }
40772         if(this.last == index){
40773             this.last = false;
40774         }
40775         if(this.lastActive == index){
40776             this.lastActive = false;
40777         }
40778         var r = this.grid.ds.getAt(index);
40779         this.selections.remove(r);
40780         if(!preventViewNotify){
40781             var view = this.grid.view ? this.grid.view : this.grid;
40782             view.onRowDeselect(index);
40783         }
40784         this.fireEvent("rowdeselect", this, index);
40785         this.fireEvent("selectionchange", this);
40786     },
40787
40788     // private
40789     restoreLast : function(){
40790         if(this._last){
40791             this.last = this._last;
40792         }
40793     },
40794
40795     // private
40796     acceptsNav : function(row, col, cm){
40797         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40798     },
40799
40800     // private
40801     onEditorKey : function(field, e){
40802         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40803         if(k == e.TAB){
40804             e.stopEvent();
40805             ed.completeEdit();
40806             if(e.shiftKey){
40807                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40808             }else{
40809                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40810             }
40811         }else if(k == e.ENTER && !e.ctrlKey){
40812             e.stopEvent();
40813             ed.completeEdit();
40814             if(e.shiftKey){
40815                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40816             }else{
40817                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40818             }
40819         }else if(k == e.ESC){
40820             ed.cancelEdit();
40821         }
40822         if(newCell){
40823             g.startEditing(newCell[0], newCell[1]);
40824         }
40825     }
40826 });/*
40827  * Based on:
40828  * Ext JS Library 1.1.1
40829  * Copyright(c) 2006-2007, Ext JS, LLC.
40830  *
40831  * Originally Released Under LGPL - original licence link has changed is not relivant.
40832  *
40833  * Fork - LGPL
40834  * <script type="text/javascript">
40835  */
40836 /**
40837  * @class Roo.grid.CellSelectionModel
40838  * @extends Roo.grid.AbstractSelectionModel
40839  * This class provides the basic implementation for cell selection in a grid.
40840  * @constructor
40841  * @param {Object} config The object containing the configuration of this model.
40842  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40843  */
40844 Roo.grid.CellSelectionModel = function(config){
40845     Roo.apply(this, config);
40846
40847     this.selection = null;
40848
40849     this.addEvents({
40850         /**
40851              * @event beforerowselect
40852              * Fires before a cell is selected.
40853              * @param {SelectionModel} this
40854              * @param {Number} rowIndex The selected row index
40855              * @param {Number} colIndex The selected cell index
40856              */
40857             "beforecellselect" : true,
40858         /**
40859              * @event cellselect
40860              * Fires when a cell is selected.
40861              * @param {SelectionModel} this
40862              * @param {Number} rowIndex The selected row index
40863              * @param {Number} colIndex The selected cell index
40864              */
40865             "cellselect" : true,
40866         /**
40867              * @event selectionchange
40868              * Fires when the active selection changes.
40869              * @param {SelectionModel} this
40870              * @param {Object} selection null for no selection or an object (o) with two properties
40871                 <ul>
40872                 <li>o.record: the record object for the row the selection is in</li>
40873                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40874                 </ul>
40875              */
40876             "selectionchange" : true,
40877         /**
40878              * @event tabend
40879              * Fires when the tab (or enter) was pressed on the last editable cell
40880              * You can use this to trigger add new row.
40881              * @param {SelectionModel} this
40882              */
40883             "tabend" : true,
40884          /**
40885              * @event beforeeditnext
40886              * Fires before the next editable sell is made active
40887              * You can use this to skip to another cell or fire the tabend
40888              *    if you set cell to false
40889              * @param {Object} eventdata object : { cell : [ row, col ] } 
40890              */
40891             "beforeeditnext" : true
40892     });
40893     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40894 };
40895
40896 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40897     
40898     enter_is_tab: false,
40899
40900     /** @ignore */
40901     initEvents : function(){
40902         this.grid.on("mousedown", this.handleMouseDown, this);
40903         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40904         var view = this.grid.view;
40905         view.on("refresh", this.onViewChange, this);
40906         view.on("rowupdated", this.onRowUpdated, this);
40907         view.on("beforerowremoved", this.clearSelections, this);
40908         view.on("beforerowsinserted", this.clearSelections, this);
40909         if(this.grid.isEditor){
40910             this.grid.on("beforeedit", this.beforeEdit,  this);
40911         }
40912     },
40913
40914         //private
40915     beforeEdit : function(e){
40916         this.select(e.row, e.column, false, true, e.record);
40917     },
40918
40919         //private
40920     onRowUpdated : function(v, index, r){
40921         if(this.selection && this.selection.record == r){
40922             v.onCellSelect(index, this.selection.cell[1]);
40923         }
40924     },
40925
40926         //private
40927     onViewChange : function(){
40928         this.clearSelections(true);
40929     },
40930
40931         /**
40932          * Returns the currently selected cell,.
40933          * @return {Array} The selected cell (row, column) or null if none selected.
40934          */
40935     getSelectedCell : function(){
40936         return this.selection ? this.selection.cell : null;
40937     },
40938
40939     /**
40940      * Clears all selections.
40941      * @param {Boolean} true to prevent the gridview from being notified about the change.
40942      */
40943     clearSelections : function(preventNotify){
40944         var s = this.selection;
40945         if(s){
40946             if(preventNotify !== true){
40947                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40948             }
40949             this.selection = null;
40950             this.fireEvent("selectionchange", this, null);
40951         }
40952     },
40953
40954     /**
40955      * Returns true if there is a selection.
40956      * @return {Boolean}
40957      */
40958     hasSelection : function(){
40959         return this.selection ? true : false;
40960     },
40961
40962     /** @ignore */
40963     handleMouseDown : function(e, t){
40964         var v = this.grid.getView();
40965         if(this.isLocked()){
40966             return;
40967         };
40968         var row = v.findRowIndex(t);
40969         var cell = v.findCellIndex(t);
40970         if(row !== false && cell !== false){
40971             this.select(row, cell);
40972         }
40973     },
40974
40975     /**
40976      * Selects a cell.
40977      * @param {Number} rowIndex
40978      * @param {Number} collIndex
40979      */
40980     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40981         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40982             this.clearSelections();
40983             r = r || this.grid.dataSource.getAt(rowIndex);
40984             this.selection = {
40985                 record : r,
40986                 cell : [rowIndex, colIndex]
40987             };
40988             if(!preventViewNotify){
40989                 var v = this.grid.getView();
40990                 v.onCellSelect(rowIndex, colIndex);
40991                 if(preventFocus !== true){
40992                     v.focusCell(rowIndex, colIndex);
40993                 }
40994             }
40995             this.fireEvent("cellselect", this, rowIndex, colIndex);
40996             this.fireEvent("selectionchange", this, this.selection);
40997         }
40998     },
40999
41000         //private
41001     isSelectable : function(rowIndex, colIndex, cm){
41002         return !cm.isHidden(colIndex);
41003     },
41004
41005     /** @ignore */
41006     handleKeyDown : function(e){
41007         //Roo.log('Cell Sel Model handleKeyDown');
41008         if(!e.isNavKeyPress()){
41009             return;
41010         }
41011         var g = this.grid, s = this.selection;
41012         if(!s){
41013             e.stopEvent();
41014             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
41015             if(cell){
41016                 this.select(cell[0], cell[1]);
41017             }
41018             return;
41019         }
41020         var sm = this;
41021         var walk = function(row, col, step){
41022             return g.walkCells(row, col, step, sm.isSelectable,  sm);
41023         };
41024         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
41025         var newCell;
41026
41027       
41028
41029         switch(k){
41030             case e.TAB:
41031                 // handled by onEditorKey
41032                 if (g.isEditor && g.editing) {
41033                     return;
41034                 }
41035                 if(e.shiftKey) {
41036                     newCell = walk(r, c-1, -1);
41037                 } else {
41038                     newCell = walk(r, c+1, 1);
41039                 }
41040                 break;
41041             
41042             case e.DOWN:
41043                newCell = walk(r+1, c, 1);
41044                 break;
41045             
41046             case e.UP:
41047                 newCell = walk(r-1, c, -1);
41048                 break;
41049             
41050             case e.RIGHT:
41051                 newCell = walk(r, c+1, 1);
41052                 break;
41053             
41054             case e.LEFT:
41055                 newCell = walk(r, c-1, -1);
41056                 break;
41057             
41058             case e.ENTER:
41059                 
41060                 if(g.isEditor && !g.editing){
41061                    g.startEditing(r, c);
41062                    e.stopEvent();
41063                    return;
41064                 }
41065                 
41066                 
41067              break;
41068         };
41069         if(newCell){
41070             this.select(newCell[0], newCell[1]);
41071             e.stopEvent();
41072             
41073         }
41074     },
41075
41076     acceptsNav : function(row, col, cm){
41077         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41078     },
41079     /**
41080      * Selects a cell.
41081      * @param {Number} field (not used) - as it's normally used as a listener
41082      * @param {Number} e - event - fake it by using
41083      *
41084      * var e = Roo.EventObjectImpl.prototype;
41085      * e.keyCode = e.TAB
41086      *
41087      * 
41088      */
41089     onEditorKey : function(field, e){
41090         
41091         var k = e.getKey(),
41092             newCell,
41093             g = this.grid,
41094             ed = g.activeEditor,
41095             forward = false;
41096         ///Roo.log('onEditorKey' + k);
41097         
41098         
41099         if (this.enter_is_tab && k == e.ENTER) {
41100             k = e.TAB;
41101         }
41102         
41103         if(k == e.TAB){
41104             if(e.shiftKey){
41105                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41106             }else{
41107                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41108                 forward = true;
41109             }
41110             
41111             e.stopEvent();
41112             
41113         } else if(k == e.ENTER &&  !e.ctrlKey){
41114             ed.completeEdit();
41115             e.stopEvent();
41116             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41117         
41118                 } else if(k == e.ESC){
41119             ed.cancelEdit();
41120         }
41121                 
41122         if (newCell) {
41123             var ecall = { cell : newCell, forward : forward };
41124             this.fireEvent('beforeeditnext', ecall );
41125             newCell = ecall.cell;
41126                         forward = ecall.forward;
41127         }
41128                 
41129         if(newCell){
41130             //Roo.log('next cell after edit');
41131             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41132         } else if (forward) {
41133             // tabbed past last
41134             this.fireEvent.defer(100, this, ['tabend',this]);
41135         }
41136     }
41137 });/*
41138  * Based on:
41139  * Ext JS Library 1.1.1
41140  * Copyright(c) 2006-2007, Ext JS, LLC.
41141  *
41142  * Originally Released Under LGPL - original licence link has changed is not relivant.
41143  *
41144  * Fork - LGPL
41145  * <script type="text/javascript">
41146  */
41147  
41148 /**
41149  * @class Roo.grid.EditorGrid
41150  * @extends Roo.grid.Grid
41151  * Class for creating and editable grid.
41152  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41153  * The container MUST have some type of size defined for the grid to fill. The container will be 
41154  * automatically set to position relative if it isn't already.
41155  * @param {Object} dataSource The data model to bind to
41156  * @param {Object} colModel The column model with info about this grid's columns
41157  */
41158 Roo.grid.EditorGrid = function(container, config){
41159     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41160     this.getGridEl().addClass("xedit-grid");
41161
41162     if(!this.selModel){
41163         this.selModel = new Roo.grid.CellSelectionModel();
41164     }
41165
41166     this.activeEditor = null;
41167
41168         this.addEvents({
41169             /**
41170              * @event beforeedit
41171              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41172              * <ul style="padding:5px;padding-left:16px;">
41173              * <li>grid - This grid</li>
41174              * <li>record - The record being edited</li>
41175              * <li>field - The field name being edited</li>
41176              * <li>value - The value for the field being edited.</li>
41177              * <li>row - The grid row index</li>
41178              * <li>column - The grid column index</li>
41179              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41180              * </ul>
41181              * @param {Object} e An edit event (see above for description)
41182              */
41183             "beforeedit" : true,
41184             /**
41185              * @event afteredit
41186              * Fires after a cell is edited. <br />
41187              * <ul style="padding:5px;padding-left:16px;">
41188              * <li>grid - This grid</li>
41189              * <li>record - The record being edited</li>
41190              * <li>field - The field name being edited</li>
41191              * <li>value - The value being set</li>
41192              * <li>originalValue - The original value for the field, before the edit.</li>
41193              * <li>row - The grid row index</li>
41194              * <li>column - The grid column index</li>
41195              * </ul>
41196              * @param {Object} e An edit event (see above for description)
41197              */
41198             "afteredit" : true,
41199             /**
41200              * @event validateedit
41201              * Fires after a cell is edited, but before the value is set in the record. 
41202          * You can use this to modify the value being set in the field, Return false
41203              * to cancel the change. The edit event object has the following properties <br />
41204              * <ul style="padding:5px;padding-left:16px;">
41205          * <li>editor - This editor</li>
41206              * <li>grid - This grid</li>
41207              * <li>record - The record being edited</li>
41208              * <li>field - The field name being edited</li>
41209              * <li>value - The value being set</li>
41210              * <li>originalValue - The original value for the field, before the edit.</li>
41211              * <li>row - The grid row index</li>
41212              * <li>column - The grid column index</li>
41213              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41214              * </ul>
41215              * @param {Object} e An edit event (see above for description)
41216              */
41217             "validateedit" : true
41218         });
41219     this.on("bodyscroll", this.stopEditing,  this);
41220     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41221 };
41222
41223 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41224     /**
41225      * @cfg {Number} clicksToEdit
41226      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41227      */
41228     clicksToEdit: 2,
41229
41230     // private
41231     isEditor : true,
41232     // private
41233     trackMouseOver: false, // causes very odd FF errors
41234
41235     onCellDblClick : function(g, row, col){
41236         this.startEditing(row, col);
41237     },
41238
41239     onEditComplete : function(ed, value, startValue){
41240         this.editing = false;
41241         this.activeEditor = null;
41242         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41243         var r = ed.record;
41244         var field = this.colModel.getDataIndex(ed.col);
41245         var e = {
41246             grid: this,
41247             record: r,
41248             field: field,
41249             originalValue: startValue,
41250             value: value,
41251             row: ed.row,
41252             column: ed.col,
41253             cancel:false,
41254             editor: ed
41255         };
41256         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41257         cell.show();
41258           
41259         if(String(value) !== String(startValue)){
41260             
41261             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41262                 r.set(field, e.value);
41263                 // if we are dealing with a combo box..
41264                 // then we also set the 'name' colum to be the displayField
41265                 if (ed.field.displayField && ed.field.name) {
41266                     r.set(ed.field.name, ed.field.el.dom.value);
41267                 }
41268                 
41269                 delete e.cancel; //?? why!!!
41270                 this.fireEvent("afteredit", e);
41271             }
41272         } else {
41273             this.fireEvent("afteredit", e); // always fire it!
41274         }
41275         this.view.focusCell(ed.row, ed.col);
41276     },
41277
41278     /**
41279      * Starts editing the specified for the specified row/column
41280      * @param {Number} rowIndex
41281      * @param {Number} colIndex
41282      */
41283     startEditing : function(row, col){
41284         this.stopEditing();
41285         if(this.colModel.isCellEditable(col, row)){
41286             this.view.ensureVisible(row, col, true);
41287           
41288             var r = this.dataSource.getAt(row);
41289             var field = this.colModel.getDataIndex(col);
41290             var cell = Roo.get(this.view.getCell(row,col));
41291             var e = {
41292                 grid: this,
41293                 record: r,
41294                 field: field,
41295                 value: r.data[field],
41296                 row: row,
41297                 column: col,
41298                 cancel:false 
41299             };
41300             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41301                 this.editing = true;
41302                 var ed = this.colModel.getCellEditor(col, row);
41303                 
41304                 if (!ed) {
41305                     return;
41306                 }
41307                 if(!ed.rendered){
41308                     ed.render(ed.parentEl || document.body);
41309                 }
41310                 ed.field.reset();
41311                
41312                 cell.hide();
41313                 
41314                 (function(){ // complex but required for focus issues in safari, ie and opera
41315                     ed.row = row;
41316                     ed.col = col;
41317                     ed.record = r;
41318                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41319                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41320                     this.activeEditor = ed;
41321                     var v = r.data[field];
41322                     ed.startEdit(this.view.getCell(row, col), v);
41323                     // combo's with 'displayField and name set
41324                     if (ed.field.displayField && ed.field.name) {
41325                         ed.field.el.dom.value = r.data[ed.field.name];
41326                     }
41327                     
41328                     
41329                 }).defer(50, this);
41330             }
41331         }
41332     },
41333         
41334     /**
41335      * Stops any active editing
41336      */
41337     stopEditing : function(){
41338         if(this.activeEditor){
41339             this.activeEditor.completeEdit();
41340         }
41341         this.activeEditor = null;
41342     },
41343         
41344          /**
41345      * Called to get grid's drag proxy text, by default returns this.ddText.
41346      * @return {String}
41347      */
41348     getDragDropText : function(){
41349         var count = this.selModel.getSelectedCell() ? 1 : 0;
41350         return String.format(this.ddText, count, count == 1 ? '' : 's');
41351     }
41352         
41353 });/*
41354  * Based on:
41355  * Ext JS Library 1.1.1
41356  * Copyright(c) 2006-2007, Ext JS, LLC.
41357  *
41358  * Originally Released Under LGPL - original licence link has changed is not relivant.
41359  *
41360  * Fork - LGPL
41361  * <script type="text/javascript">
41362  */
41363
41364 // private - not really -- you end up using it !
41365 // This is a support class used internally by the Grid components
41366
41367 /**
41368  * @class Roo.grid.GridEditor
41369  * @extends Roo.Editor
41370  * Class for creating and editable grid elements.
41371  * @param {Object} config any settings (must include field)
41372  */
41373 Roo.grid.GridEditor = function(field, config){
41374     if (!config && field.field) {
41375         config = field;
41376         field = Roo.factory(config.field, Roo.form);
41377     }
41378     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41379     field.monitorTab = false;
41380 };
41381
41382 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41383     
41384     /**
41385      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41386      */
41387     
41388     alignment: "tl-tl",
41389     autoSize: "width",
41390     hideEl : false,
41391     cls: "x-small-editor x-grid-editor",
41392     shim:false,
41393     shadow:"frame"
41394 });/*
41395  * Based on:
41396  * Ext JS Library 1.1.1
41397  * Copyright(c) 2006-2007, Ext JS, LLC.
41398  *
41399  * Originally Released Under LGPL - original licence link has changed is not relivant.
41400  *
41401  * Fork - LGPL
41402  * <script type="text/javascript">
41403  */
41404   
41405
41406   
41407 Roo.grid.PropertyRecord = Roo.data.Record.create([
41408     {name:'name',type:'string'},  'value'
41409 ]);
41410
41411
41412 Roo.grid.PropertyStore = function(grid, source){
41413     this.grid = grid;
41414     this.store = new Roo.data.Store({
41415         recordType : Roo.grid.PropertyRecord
41416     });
41417     this.store.on('update', this.onUpdate,  this);
41418     if(source){
41419         this.setSource(source);
41420     }
41421     Roo.grid.PropertyStore.superclass.constructor.call(this);
41422 };
41423
41424
41425
41426 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41427     setSource : function(o){
41428         this.source = o;
41429         this.store.removeAll();
41430         var data = [];
41431         for(var k in o){
41432             if(this.isEditableValue(o[k])){
41433                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41434             }
41435         }
41436         this.store.loadRecords({records: data}, {}, true);
41437     },
41438
41439     onUpdate : function(ds, record, type){
41440         if(type == Roo.data.Record.EDIT){
41441             var v = record.data['value'];
41442             var oldValue = record.modified['value'];
41443             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41444                 this.source[record.id] = v;
41445                 record.commit();
41446                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41447             }else{
41448                 record.reject();
41449             }
41450         }
41451     },
41452
41453     getProperty : function(row){
41454        return this.store.getAt(row);
41455     },
41456
41457     isEditableValue: function(val){
41458         if(val && val instanceof Date){
41459             return true;
41460         }else if(typeof val == 'object' || typeof val == 'function'){
41461             return false;
41462         }
41463         return true;
41464     },
41465
41466     setValue : function(prop, value){
41467         this.source[prop] = value;
41468         this.store.getById(prop).set('value', value);
41469     },
41470
41471     getSource : function(){
41472         return this.source;
41473     }
41474 });
41475
41476 Roo.grid.PropertyColumnModel = function(grid, store){
41477     this.grid = grid;
41478     var g = Roo.grid;
41479     g.PropertyColumnModel.superclass.constructor.call(this, [
41480         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41481         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41482     ]);
41483     this.store = store;
41484     this.bselect = Roo.DomHelper.append(document.body, {
41485         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41486             {tag: 'option', value: 'true', html: 'true'},
41487             {tag: 'option', value: 'false', html: 'false'}
41488         ]
41489     });
41490     Roo.id(this.bselect);
41491     var f = Roo.form;
41492     this.editors = {
41493         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41494         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41495         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41496         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41497         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41498     };
41499     this.renderCellDelegate = this.renderCell.createDelegate(this);
41500     this.renderPropDelegate = this.renderProp.createDelegate(this);
41501 };
41502
41503 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41504     
41505     
41506     nameText : 'Name',
41507     valueText : 'Value',
41508     
41509     dateFormat : 'm/j/Y',
41510     
41511     
41512     renderDate : function(dateVal){
41513         return dateVal.dateFormat(this.dateFormat);
41514     },
41515
41516     renderBool : function(bVal){
41517         return bVal ? 'true' : 'false';
41518     },
41519
41520     isCellEditable : function(colIndex, rowIndex){
41521         return colIndex == 1;
41522     },
41523
41524     getRenderer : function(col){
41525         return col == 1 ?
41526             this.renderCellDelegate : this.renderPropDelegate;
41527     },
41528
41529     renderProp : function(v){
41530         return this.getPropertyName(v);
41531     },
41532
41533     renderCell : function(val){
41534         var rv = val;
41535         if(val instanceof Date){
41536             rv = this.renderDate(val);
41537         }else if(typeof val == 'boolean'){
41538             rv = this.renderBool(val);
41539         }
41540         return Roo.util.Format.htmlEncode(rv);
41541     },
41542
41543     getPropertyName : function(name){
41544         var pn = this.grid.propertyNames;
41545         return pn && pn[name] ? pn[name] : name;
41546     },
41547
41548     getCellEditor : function(colIndex, rowIndex){
41549         var p = this.store.getProperty(rowIndex);
41550         var n = p.data['name'], val = p.data['value'];
41551         
41552         if(typeof(this.grid.customEditors[n]) == 'string'){
41553             return this.editors[this.grid.customEditors[n]];
41554         }
41555         if(typeof(this.grid.customEditors[n]) != 'undefined'){
41556             return this.grid.customEditors[n];
41557         }
41558         if(val instanceof Date){
41559             return this.editors['date'];
41560         }else if(typeof val == 'number'){
41561             return this.editors['number'];
41562         }else if(typeof val == 'boolean'){
41563             return this.editors['boolean'];
41564         }else{
41565             return this.editors['string'];
41566         }
41567     }
41568 });
41569
41570 /**
41571  * @class Roo.grid.PropertyGrid
41572  * @extends Roo.grid.EditorGrid
41573  * This class represents the  interface of a component based property grid control.
41574  * <br><br>Usage:<pre><code>
41575  var grid = new Roo.grid.PropertyGrid("my-container-id", {
41576       
41577  });
41578  // set any options
41579  grid.render();
41580  * </code></pre>
41581   
41582  * @constructor
41583  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41584  * The container MUST have some type of size defined for the grid to fill. The container will be
41585  * automatically set to position relative if it isn't already.
41586  * @param {Object} config A config object that sets properties on this grid.
41587  */
41588 Roo.grid.PropertyGrid = function(container, config){
41589     config = config || {};
41590     var store = new Roo.grid.PropertyStore(this);
41591     this.store = store;
41592     var cm = new Roo.grid.PropertyColumnModel(this, store);
41593     store.store.sort('name', 'ASC');
41594     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41595         ds: store.store,
41596         cm: cm,
41597         enableColLock:false,
41598         enableColumnMove:false,
41599         stripeRows:false,
41600         trackMouseOver: false,
41601         clicksToEdit:1
41602     }, config));
41603     this.getGridEl().addClass('x-props-grid');
41604     this.lastEditRow = null;
41605     this.on('columnresize', this.onColumnResize, this);
41606     this.addEvents({
41607          /**
41608              * @event beforepropertychange
41609              * Fires before a property changes (return false to stop?)
41610              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41611              * @param {String} id Record Id
41612              * @param {String} newval New Value
41613          * @param {String} oldval Old Value
41614              */
41615         "beforepropertychange": true,
41616         /**
41617              * @event propertychange
41618              * Fires after a property changes
41619              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41620              * @param {String} id Record Id
41621              * @param {String} newval New Value
41622          * @param {String} oldval Old Value
41623              */
41624         "propertychange": true
41625     });
41626     this.customEditors = this.customEditors || {};
41627 };
41628 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41629     
41630      /**
41631      * @cfg {Object} customEditors map of colnames=> custom editors.
41632      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41633      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41634      * false disables editing of the field.
41635          */
41636     
41637       /**
41638      * @cfg {Object} propertyNames map of property Names to their displayed value
41639          */
41640     
41641     render : function(){
41642         Roo.grid.PropertyGrid.superclass.render.call(this);
41643         this.autoSize.defer(100, this);
41644     },
41645
41646     autoSize : function(){
41647         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41648         if(this.view){
41649             this.view.fitColumns();
41650         }
41651     },
41652
41653     onColumnResize : function(){
41654         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41655         this.autoSize();
41656     },
41657     /**
41658      * Sets the data for the Grid
41659      * accepts a Key => Value object of all the elements avaiable.
41660      * @param {Object} data  to appear in grid.
41661      */
41662     setSource : function(source){
41663         this.store.setSource(source);
41664         //this.autoSize();
41665     },
41666     /**
41667      * Gets all the data from the grid.
41668      * @return {Object} data  data stored in grid
41669      */
41670     getSource : function(){
41671         return this.store.getSource();
41672     }
41673 });/*
41674   
41675  * Licence LGPL
41676  
41677  */
41678  
41679 /**
41680  * @class Roo.grid.Calendar
41681  * @extends Roo.grid.Grid
41682  * This class extends the Grid to provide a calendar widget
41683  * <br><br>Usage:<pre><code>
41684  var grid = new Roo.grid.Calendar("my-container-id", {
41685      ds: myDataStore,
41686      cm: myColModel,
41687      selModel: mySelectionModel,
41688      autoSizeColumns: true,
41689      monitorWindowResize: false,
41690      trackMouseOver: true
41691      eventstore : real data store..
41692  });
41693  // set any options
41694  grid.render();
41695   
41696   * @constructor
41697  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41698  * The container MUST have some type of size defined for the grid to fill. The container will be
41699  * automatically set to position relative if it isn't already.
41700  * @param {Object} config A config object that sets properties on this grid.
41701  */
41702 Roo.grid.Calendar = function(container, config){
41703         // initialize the container
41704         this.container = Roo.get(container);
41705         this.container.update("");
41706         this.container.setStyle("overflow", "hidden");
41707     this.container.addClass('x-grid-container');
41708
41709     this.id = this.container.id;
41710
41711     Roo.apply(this, config);
41712     // check and correct shorthanded configs
41713     
41714     var rows = [];
41715     var d =1;
41716     for (var r = 0;r < 6;r++) {
41717         
41718         rows[r]=[];
41719         for (var c =0;c < 7;c++) {
41720             rows[r][c]= '';
41721         }
41722     }
41723     if (this.eventStore) {
41724         this.eventStore= Roo.factory(this.eventStore, Roo.data);
41725         this.eventStore.on('load',this.onLoad, this);
41726         this.eventStore.on('beforeload',this.clearEvents, this);
41727          
41728     }
41729     
41730     this.dataSource = new Roo.data.Store({
41731             proxy: new Roo.data.MemoryProxy(rows),
41732             reader: new Roo.data.ArrayReader({}, [
41733                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41734     });
41735
41736     this.dataSource.load();
41737     this.ds = this.dataSource;
41738     this.ds.xmodule = this.xmodule || false;
41739     
41740     
41741     var cellRender = function(v,x,r)
41742     {
41743         return String.format(
41744             '<div class="fc-day  fc-widget-content"><div>' +
41745                 '<div class="fc-event-container"></div>' +
41746                 '<div class="fc-day-number">{0}</div>'+
41747                 
41748                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41749             '</div></div>', v);
41750     
41751     }
41752     
41753     
41754     this.colModel = new Roo.grid.ColumnModel( [
41755         {
41756             xtype: 'ColumnModel',
41757             xns: Roo.grid,
41758             dataIndex : 'weekday0',
41759             header : 'Sunday',
41760             renderer : cellRender
41761         },
41762         {
41763             xtype: 'ColumnModel',
41764             xns: Roo.grid,
41765             dataIndex : 'weekday1',
41766             header : 'Monday',
41767             renderer : cellRender
41768         },
41769         {
41770             xtype: 'ColumnModel',
41771             xns: Roo.grid,
41772             dataIndex : 'weekday2',
41773             header : 'Tuesday',
41774             renderer : cellRender
41775         },
41776         {
41777             xtype: 'ColumnModel',
41778             xns: Roo.grid,
41779             dataIndex : 'weekday3',
41780             header : 'Wednesday',
41781             renderer : cellRender
41782         },
41783         {
41784             xtype: 'ColumnModel',
41785             xns: Roo.grid,
41786             dataIndex : 'weekday4',
41787             header : 'Thursday',
41788             renderer : cellRender
41789         },
41790         {
41791             xtype: 'ColumnModel',
41792             xns: Roo.grid,
41793             dataIndex : 'weekday5',
41794             header : 'Friday',
41795             renderer : cellRender
41796         },
41797         {
41798             xtype: 'ColumnModel',
41799             xns: Roo.grid,
41800             dataIndex : 'weekday6',
41801             header : 'Saturday',
41802             renderer : cellRender
41803         }
41804     ]);
41805     this.cm = this.colModel;
41806     this.cm.xmodule = this.xmodule || false;
41807  
41808         
41809           
41810     //this.selModel = new Roo.grid.CellSelectionModel();
41811     //this.sm = this.selModel;
41812     //this.selModel.init(this);
41813     
41814     
41815     if(this.width){
41816         this.container.setWidth(this.width);
41817     }
41818
41819     if(this.height){
41820         this.container.setHeight(this.height);
41821     }
41822     /** @private */
41823         this.addEvents({
41824         // raw events
41825         /**
41826          * @event click
41827          * The raw click event for the entire grid.
41828          * @param {Roo.EventObject} e
41829          */
41830         "click" : true,
41831         /**
41832          * @event dblclick
41833          * The raw dblclick event for the entire grid.
41834          * @param {Roo.EventObject} e
41835          */
41836         "dblclick" : true,
41837         /**
41838          * @event contextmenu
41839          * The raw contextmenu event for the entire grid.
41840          * @param {Roo.EventObject} e
41841          */
41842         "contextmenu" : true,
41843         /**
41844          * @event mousedown
41845          * The raw mousedown event for the entire grid.
41846          * @param {Roo.EventObject} e
41847          */
41848         "mousedown" : true,
41849         /**
41850          * @event mouseup
41851          * The raw mouseup event for the entire grid.
41852          * @param {Roo.EventObject} e
41853          */
41854         "mouseup" : true,
41855         /**
41856          * @event mouseover
41857          * The raw mouseover event for the entire grid.
41858          * @param {Roo.EventObject} e
41859          */
41860         "mouseover" : true,
41861         /**
41862          * @event mouseout
41863          * The raw mouseout event for the entire grid.
41864          * @param {Roo.EventObject} e
41865          */
41866         "mouseout" : true,
41867         /**
41868          * @event keypress
41869          * The raw keypress event for the entire grid.
41870          * @param {Roo.EventObject} e
41871          */
41872         "keypress" : true,
41873         /**
41874          * @event keydown
41875          * The raw keydown event for the entire grid.
41876          * @param {Roo.EventObject} e
41877          */
41878         "keydown" : true,
41879
41880         // custom events
41881
41882         /**
41883          * @event cellclick
41884          * Fires when a cell is clicked
41885          * @param {Grid} this
41886          * @param {Number} rowIndex
41887          * @param {Number} columnIndex
41888          * @param {Roo.EventObject} e
41889          */
41890         "cellclick" : true,
41891         /**
41892          * @event celldblclick
41893          * Fires when a cell is double clicked
41894          * @param {Grid} this
41895          * @param {Number} rowIndex
41896          * @param {Number} columnIndex
41897          * @param {Roo.EventObject} e
41898          */
41899         "celldblclick" : true,
41900         /**
41901          * @event rowclick
41902          * Fires when a row is clicked
41903          * @param {Grid} this
41904          * @param {Number} rowIndex
41905          * @param {Roo.EventObject} e
41906          */
41907         "rowclick" : true,
41908         /**
41909          * @event rowdblclick
41910          * Fires when a row is double clicked
41911          * @param {Grid} this
41912          * @param {Number} rowIndex
41913          * @param {Roo.EventObject} e
41914          */
41915         "rowdblclick" : true,
41916         /**
41917          * @event headerclick
41918          * Fires when a header is clicked
41919          * @param {Grid} this
41920          * @param {Number} columnIndex
41921          * @param {Roo.EventObject} e
41922          */
41923         "headerclick" : true,
41924         /**
41925          * @event headerdblclick
41926          * Fires when a header cell is double clicked
41927          * @param {Grid} this
41928          * @param {Number} columnIndex
41929          * @param {Roo.EventObject} e
41930          */
41931         "headerdblclick" : true,
41932         /**
41933          * @event rowcontextmenu
41934          * Fires when a row is right clicked
41935          * @param {Grid} this
41936          * @param {Number} rowIndex
41937          * @param {Roo.EventObject} e
41938          */
41939         "rowcontextmenu" : true,
41940         /**
41941          * @event cellcontextmenu
41942          * Fires when a cell is right clicked
41943          * @param {Grid} this
41944          * @param {Number} rowIndex
41945          * @param {Number} cellIndex
41946          * @param {Roo.EventObject} e
41947          */
41948          "cellcontextmenu" : true,
41949         /**
41950          * @event headercontextmenu
41951          * Fires when a header is right clicked
41952          * @param {Grid} this
41953          * @param {Number} columnIndex
41954          * @param {Roo.EventObject} e
41955          */
41956         "headercontextmenu" : true,
41957         /**
41958          * @event bodyscroll
41959          * Fires when the body element is scrolled
41960          * @param {Number} scrollLeft
41961          * @param {Number} scrollTop
41962          */
41963         "bodyscroll" : true,
41964         /**
41965          * @event columnresize
41966          * Fires when the user resizes a column
41967          * @param {Number} columnIndex
41968          * @param {Number} newSize
41969          */
41970         "columnresize" : true,
41971         /**
41972          * @event columnmove
41973          * Fires when the user moves a column
41974          * @param {Number} oldIndex
41975          * @param {Number} newIndex
41976          */
41977         "columnmove" : true,
41978         /**
41979          * @event startdrag
41980          * Fires when row(s) start being dragged
41981          * @param {Grid} this
41982          * @param {Roo.GridDD} dd The drag drop object
41983          * @param {event} e The raw browser event
41984          */
41985         "startdrag" : true,
41986         /**
41987          * @event enddrag
41988          * Fires when a drag operation is complete
41989          * @param {Grid} this
41990          * @param {Roo.GridDD} dd The drag drop object
41991          * @param {event} e The raw browser event
41992          */
41993         "enddrag" : true,
41994         /**
41995          * @event dragdrop
41996          * Fires when dragged row(s) are dropped on a valid DD target
41997          * @param {Grid} this
41998          * @param {Roo.GridDD} dd The drag drop object
41999          * @param {String} targetId The target drag drop object
42000          * @param {event} e The raw browser event
42001          */
42002         "dragdrop" : true,
42003         /**
42004          * @event dragover
42005          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
42006          * @param {Grid} this
42007          * @param {Roo.GridDD} dd The drag drop object
42008          * @param {String} targetId The target drag drop object
42009          * @param {event} e The raw browser event
42010          */
42011         "dragover" : true,
42012         /**
42013          * @event dragenter
42014          *  Fires when the dragged row(s) first cross another DD target while being dragged
42015          * @param {Grid} this
42016          * @param {Roo.GridDD} dd The drag drop object
42017          * @param {String} targetId The target drag drop object
42018          * @param {event} e The raw browser event
42019          */
42020         "dragenter" : true,
42021         /**
42022          * @event dragout
42023          * Fires when the dragged row(s) leave another DD target while being dragged
42024          * @param {Grid} this
42025          * @param {Roo.GridDD} dd The drag drop object
42026          * @param {String} targetId The target drag drop object
42027          * @param {event} e The raw browser event
42028          */
42029         "dragout" : true,
42030         /**
42031          * @event rowclass
42032          * Fires when a row is rendered, so you can change add a style to it.
42033          * @param {GridView} gridview   The grid view
42034          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
42035          */
42036         'rowclass' : true,
42037
42038         /**
42039          * @event render
42040          * Fires when the grid is rendered
42041          * @param {Grid} grid
42042          */
42043         'render' : true,
42044             /**
42045              * @event select
42046              * Fires when a date is selected
42047              * @param {DatePicker} this
42048              * @param {Date} date The selected date
42049              */
42050         'select': true,
42051         /**
42052              * @event monthchange
42053              * Fires when the displayed month changes 
42054              * @param {DatePicker} this
42055              * @param {Date} date The selected month
42056              */
42057         'monthchange': true,
42058         /**
42059              * @event evententer
42060              * Fires when mouse over an event
42061              * @param {Calendar} this
42062              * @param {event} Event
42063              */
42064         'evententer': true,
42065         /**
42066              * @event eventleave
42067              * Fires when the mouse leaves an
42068              * @param {Calendar} this
42069              * @param {event}
42070              */
42071         'eventleave': true,
42072         /**
42073              * @event eventclick
42074              * Fires when the mouse click an
42075              * @param {Calendar} this
42076              * @param {event}
42077              */
42078         'eventclick': true,
42079         /**
42080              * @event eventrender
42081              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42082              * @param {Calendar} this
42083              * @param {data} data to be modified
42084              */
42085         'eventrender': true
42086         
42087     });
42088
42089     Roo.grid.Grid.superclass.constructor.call(this);
42090     this.on('render', function() {
42091         this.view.el.addClass('x-grid-cal'); 
42092         
42093         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42094
42095     },this);
42096     
42097     if (!Roo.grid.Calendar.style) {
42098         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42099             
42100             
42101             '.x-grid-cal .x-grid-col' :  {
42102                 height: 'auto !important',
42103                 'vertical-align': 'top'
42104             },
42105             '.x-grid-cal  .fc-event-hori' : {
42106                 height: '14px'
42107             }
42108              
42109             
42110         }, Roo.id());
42111     }
42112
42113     
42114     
42115 };
42116 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42117     /**
42118      * @cfg {Store} eventStore The store that loads events.
42119      */
42120     eventStore : 25,
42121
42122      
42123     activeDate : false,
42124     startDay : 0,
42125     autoWidth : true,
42126     monitorWindowResize : false,
42127
42128     
42129     resizeColumns : function() {
42130         var col = (this.view.el.getWidth() / 7) - 3;
42131         // loop through cols, and setWidth
42132         for(var i =0 ; i < 7 ; i++){
42133             this.cm.setColumnWidth(i, col);
42134         }
42135     },
42136      setDate :function(date) {
42137         
42138         Roo.log('setDate?');
42139         
42140         this.resizeColumns();
42141         var vd = this.activeDate;
42142         this.activeDate = date;
42143 //        if(vd && this.el){
42144 //            var t = date.getTime();
42145 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42146 //                Roo.log('using add remove');
42147 //                
42148 //                this.fireEvent('monthchange', this, date);
42149 //                
42150 //                this.cells.removeClass("fc-state-highlight");
42151 //                this.cells.each(function(c){
42152 //                   if(c.dateValue == t){
42153 //                       c.addClass("fc-state-highlight");
42154 //                       setTimeout(function(){
42155 //                            try{c.dom.firstChild.focus();}catch(e){}
42156 //                       }, 50);
42157 //                       return false;
42158 //                   }
42159 //                   return true;
42160 //                });
42161 //                return;
42162 //            }
42163 //        }
42164         
42165         var days = date.getDaysInMonth();
42166         
42167         var firstOfMonth = date.getFirstDateOfMonth();
42168         var startingPos = firstOfMonth.getDay()-this.startDay;
42169         
42170         if(startingPos < this.startDay){
42171             startingPos += 7;
42172         }
42173         
42174         var pm = date.add(Date.MONTH, -1);
42175         var prevStart = pm.getDaysInMonth()-startingPos;
42176 //        
42177         
42178         
42179         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42180         
42181         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42182         //this.cells.addClassOnOver('fc-state-hover');
42183         
42184         var cells = this.cells.elements;
42185         var textEls = this.textNodes;
42186         
42187         //Roo.each(cells, function(cell){
42188         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42189         //});
42190         
42191         days += startingPos;
42192
42193         // convert everything to numbers so it's fast
42194         var day = 86400000;
42195         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42196         //Roo.log(d);
42197         //Roo.log(pm);
42198         //Roo.log(prevStart);
42199         
42200         var today = new Date().clearTime().getTime();
42201         var sel = date.clearTime().getTime();
42202         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42203         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42204         var ddMatch = this.disabledDatesRE;
42205         var ddText = this.disabledDatesText;
42206         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42207         var ddaysText = this.disabledDaysText;
42208         var format = this.format;
42209         
42210         var setCellClass = function(cal, cell){
42211             
42212             //Roo.log('set Cell Class');
42213             cell.title = "";
42214             var t = d.getTime();
42215             
42216             //Roo.log(d);
42217             
42218             
42219             cell.dateValue = t;
42220             if(t == today){
42221                 cell.className += " fc-today";
42222                 cell.className += " fc-state-highlight";
42223                 cell.title = cal.todayText;
42224             }
42225             if(t == sel){
42226                 // disable highlight in other month..
42227                 cell.className += " fc-state-highlight";
42228                 
42229             }
42230             // disabling
42231             if(t < min) {
42232                 //cell.className = " fc-state-disabled";
42233                 cell.title = cal.minText;
42234                 return;
42235             }
42236             if(t > max) {
42237                 //cell.className = " fc-state-disabled";
42238                 cell.title = cal.maxText;
42239                 return;
42240             }
42241             if(ddays){
42242                 if(ddays.indexOf(d.getDay()) != -1){
42243                     // cell.title = ddaysText;
42244                    // cell.className = " fc-state-disabled";
42245                 }
42246             }
42247             if(ddMatch && format){
42248                 var fvalue = d.dateFormat(format);
42249                 if(ddMatch.test(fvalue)){
42250                     cell.title = ddText.replace("%0", fvalue);
42251                    cell.className = " fc-state-disabled";
42252                 }
42253             }
42254             
42255             if (!cell.initialClassName) {
42256                 cell.initialClassName = cell.dom.className;
42257             }
42258             
42259             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42260         };
42261
42262         var i = 0;
42263         
42264         for(; i < startingPos; i++) {
42265             cells[i].dayName =  (++prevStart);
42266             Roo.log(textEls[i]);
42267             d.setDate(d.getDate()+1);
42268             
42269             //cells[i].className = "fc-past fc-other-month";
42270             setCellClass(this, cells[i]);
42271         }
42272         
42273         var intDay = 0;
42274         
42275         for(; i < days; i++){
42276             intDay = i - startingPos + 1;
42277             cells[i].dayName =  (intDay);
42278             d.setDate(d.getDate()+1);
42279             
42280             cells[i].className = ''; // "x-date-active";
42281             setCellClass(this, cells[i]);
42282         }
42283         var extraDays = 0;
42284         
42285         for(; i < 42; i++) {
42286             //textEls[i].innerHTML = (++extraDays);
42287             
42288             d.setDate(d.getDate()+1);
42289             cells[i].dayName = (++extraDays);
42290             cells[i].className = "fc-future fc-other-month";
42291             setCellClass(this, cells[i]);
42292         }
42293         
42294         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42295         
42296         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42297         
42298         // this will cause all the cells to mis
42299         var rows= [];
42300         var i =0;
42301         for (var r = 0;r < 6;r++) {
42302             for (var c =0;c < 7;c++) {
42303                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42304             }    
42305         }
42306         
42307         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42308         for(i=0;i<cells.length;i++) {
42309             
42310             this.cells.elements[i].dayName = cells[i].dayName ;
42311             this.cells.elements[i].className = cells[i].className;
42312             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42313             this.cells.elements[i].title = cells[i].title ;
42314             this.cells.elements[i].dateValue = cells[i].dateValue ;
42315         }
42316         
42317         
42318         
42319         
42320         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42321         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42322         
42323         ////if(totalRows != 6){
42324             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42325            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42326        // }
42327         
42328         this.fireEvent('monthchange', this, date);
42329         
42330         
42331     },
42332  /**
42333      * Returns the grid's SelectionModel.
42334      * @return {SelectionModel}
42335      */
42336     getSelectionModel : function(){
42337         if(!this.selModel){
42338             this.selModel = new Roo.grid.CellSelectionModel();
42339         }
42340         return this.selModel;
42341     },
42342
42343     load: function() {
42344         this.eventStore.load()
42345         
42346         
42347         
42348     },
42349     
42350     findCell : function(dt) {
42351         dt = dt.clearTime().getTime();
42352         var ret = false;
42353         this.cells.each(function(c){
42354             //Roo.log("check " +c.dateValue + '?=' + dt);
42355             if(c.dateValue == dt){
42356                 ret = c;
42357                 return false;
42358             }
42359             return true;
42360         });
42361         
42362         return ret;
42363     },
42364     
42365     findCells : function(rec) {
42366         var s = rec.data.start_dt.clone().clearTime().getTime();
42367        // Roo.log(s);
42368         var e= rec.data.end_dt.clone().clearTime().getTime();
42369        // Roo.log(e);
42370         var ret = [];
42371         this.cells.each(function(c){
42372              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42373             
42374             if(c.dateValue > e){
42375                 return ;
42376             }
42377             if(c.dateValue < s){
42378                 return ;
42379             }
42380             ret.push(c);
42381         });
42382         
42383         return ret;    
42384     },
42385     
42386     findBestRow: function(cells)
42387     {
42388         var ret = 0;
42389         
42390         for (var i =0 ; i < cells.length;i++) {
42391             ret  = Math.max(cells[i].rows || 0,ret);
42392         }
42393         return ret;
42394         
42395     },
42396     
42397     
42398     addItem : function(rec)
42399     {
42400         // look for vertical location slot in
42401         var cells = this.findCells(rec);
42402         
42403         rec.row = this.findBestRow(cells);
42404         
42405         // work out the location.
42406         
42407         var crow = false;
42408         var rows = [];
42409         for(var i =0; i < cells.length; i++) {
42410             if (!crow) {
42411                 crow = {
42412                     start : cells[i],
42413                     end :  cells[i]
42414                 };
42415                 continue;
42416             }
42417             if (crow.start.getY() == cells[i].getY()) {
42418                 // on same row.
42419                 crow.end = cells[i];
42420                 continue;
42421             }
42422             // different row.
42423             rows.push(crow);
42424             crow = {
42425                 start: cells[i],
42426                 end : cells[i]
42427             };
42428             
42429         }
42430         
42431         rows.push(crow);
42432         rec.els = [];
42433         rec.rows = rows;
42434         rec.cells = cells;
42435         for (var i = 0; i < cells.length;i++) {
42436             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42437             
42438         }
42439         
42440         
42441     },
42442     
42443     clearEvents: function() {
42444         
42445         if (!this.eventStore.getCount()) {
42446             return;
42447         }
42448         // reset number of rows in cells.
42449         Roo.each(this.cells.elements, function(c){
42450             c.rows = 0;
42451         });
42452         
42453         this.eventStore.each(function(e) {
42454             this.clearEvent(e);
42455         },this);
42456         
42457     },
42458     
42459     clearEvent : function(ev)
42460     {
42461         if (ev.els) {
42462             Roo.each(ev.els, function(el) {
42463                 el.un('mouseenter' ,this.onEventEnter, this);
42464                 el.un('mouseleave' ,this.onEventLeave, this);
42465                 el.remove();
42466             },this);
42467             ev.els = [];
42468         }
42469     },
42470     
42471     
42472     renderEvent : function(ev,ctr) {
42473         if (!ctr) {
42474              ctr = this.view.el.select('.fc-event-container',true).first();
42475         }
42476         
42477          
42478         this.clearEvent(ev);
42479             //code
42480        
42481         
42482         
42483         ev.els = [];
42484         var cells = ev.cells;
42485         var rows = ev.rows;
42486         this.fireEvent('eventrender', this, ev);
42487         
42488         for(var i =0; i < rows.length; i++) {
42489             
42490             cls = '';
42491             if (i == 0) {
42492                 cls += ' fc-event-start';
42493             }
42494             if ((i+1) == rows.length) {
42495                 cls += ' fc-event-end';
42496             }
42497             
42498             //Roo.log(ev.data);
42499             // how many rows should it span..
42500             var cg = this.eventTmpl.append(ctr,Roo.apply({
42501                 fccls : cls
42502                 
42503             }, ev.data) , true);
42504             
42505             
42506             cg.on('mouseenter' ,this.onEventEnter, this, ev);
42507             cg.on('mouseleave' ,this.onEventLeave, this, ev);
42508             cg.on('click', this.onEventClick, this, ev);
42509             
42510             ev.els.push(cg);
42511             
42512             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42513             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42514             //Roo.log(cg);
42515              
42516             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
42517             cg.setWidth(ebox.right - sbox.x -2);
42518         }
42519     },
42520     
42521     renderEvents: function()
42522     {   
42523         // first make sure there is enough space..
42524         
42525         if (!this.eventTmpl) {
42526             this.eventTmpl = new Roo.Template(
42527                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
42528                     '<div class="fc-event-inner">' +
42529                         '<span class="fc-event-time">{time}</span>' +
42530                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42531                     '</div>' +
42532                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
42533                 '</div>'
42534             );
42535                 
42536         }
42537                
42538         
42539         
42540         this.cells.each(function(c) {
42541             //Roo.log(c.select('.fc-day-content div',true).first());
42542             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42543         });
42544         
42545         var ctr = this.view.el.select('.fc-event-container',true).first();
42546         
42547         var cls;
42548         this.eventStore.each(function(ev){
42549             
42550             this.renderEvent(ev);
42551              
42552              
42553         }, this);
42554         this.view.layout();
42555         
42556     },
42557     
42558     onEventEnter: function (e, el,event,d) {
42559         this.fireEvent('evententer', this, el, event);
42560     },
42561     
42562     onEventLeave: function (e, el,event,d) {
42563         this.fireEvent('eventleave', this, el, event);
42564     },
42565     
42566     onEventClick: function (e, el,event,d) {
42567         this.fireEvent('eventclick', this, el, event);
42568     },
42569     
42570     onMonthChange: function () {
42571         this.store.load();
42572     },
42573     
42574     onLoad: function () {
42575         
42576         //Roo.log('calendar onload');
42577 //         
42578         if(this.eventStore.getCount() > 0){
42579             
42580            
42581             
42582             this.eventStore.each(function(d){
42583                 
42584                 
42585                 // FIXME..
42586                 var add =   d.data;
42587                 if (typeof(add.end_dt) == 'undefined')  {
42588                     Roo.log("Missing End time in calendar data: ");
42589                     Roo.log(d);
42590                     return;
42591                 }
42592                 if (typeof(add.start_dt) == 'undefined')  {
42593                     Roo.log("Missing Start time in calendar data: ");
42594                     Roo.log(d);
42595                     return;
42596                 }
42597                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42598                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42599                 add.id = add.id || d.id;
42600                 add.title = add.title || '??';
42601                 
42602                 this.addItem(d);
42603                 
42604              
42605             },this);
42606         }
42607         
42608         this.renderEvents();
42609     }
42610     
42611
42612 });
42613 /*
42614  grid : {
42615                 xtype: 'Grid',
42616                 xns: Roo.grid,
42617                 listeners : {
42618                     render : function ()
42619                     {
42620                         _this.grid = this;
42621                         
42622                         if (!this.view.el.hasClass('course-timesheet')) {
42623                             this.view.el.addClass('course-timesheet');
42624                         }
42625                         if (this.tsStyle) {
42626                             this.ds.load({});
42627                             return; 
42628                         }
42629                         Roo.log('width');
42630                         Roo.log(_this.grid.view.el.getWidth());
42631                         
42632                         
42633                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
42634                             '.course-timesheet .x-grid-row' : {
42635                                 height: '80px'
42636                             },
42637                             '.x-grid-row td' : {
42638                                 'vertical-align' : 0
42639                             },
42640                             '.course-edit-link' : {
42641                                 'color' : 'blue',
42642                                 'text-overflow' : 'ellipsis',
42643                                 'overflow' : 'hidden',
42644                                 'white-space' : 'nowrap',
42645                                 'cursor' : 'pointer'
42646                             },
42647                             '.sub-link' : {
42648                                 'color' : 'green'
42649                             },
42650                             '.de-act-sup-link' : {
42651                                 'color' : 'purple',
42652                                 'text-decoration' : 'line-through'
42653                             },
42654                             '.de-act-link' : {
42655                                 'color' : 'red',
42656                                 'text-decoration' : 'line-through'
42657                             },
42658                             '.course-timesheet .course-highlight' : {
42659                                 'border-top-style': 'dashed !important',
42660                                 'border-bottom-bottom': 'dashed !important'
42661                             },
42662                             '.course-timesheet .course-item' : {
42663                                 'font-family'   : 'tahoma, arial, helvetica',
42664                                 'font-size'     : '11px',
42665                                 'overflow'      : 'hidden',
42666                                 'padding-left'  : '10px',
42667                                 'padding-right' : '10px',
42668                                 'padding-top' : '10px' 
42669                             }
42670                             
42671                         }, Roo.id());
42672                                 this.ds.load({});
42673                     }
42674                 },
42675                 autoWidth : true,
42676                 monitorWindowResize : false,
42677                 cellrenderer : function(v,x,r)
42678                 {
42679                     return v;
42680                 },
42681                 sm : {
42682                     xtype: 'CellSelectionModel',
42683                     xns: Roo.grid
42684                 },
42685                 dataSource : {
42686                     xtype: 'Store',
42687                     xns: Roo.data,
42688                     listeners : {
42689                         beforeload : function (_self, options)
42690                         {
42691                             options.params = options.params || {};
42692                             options.params._month = _this.monthField.getValue();
42693                             options.params.limit = 9999;
42694                             options.params['sort'] = 'when_dt';    
42695                             options.params['dir'] = 'ASC';    
42696                             this.proxy.loadResponse = this.loadResponse;
42697                             Roo.log("load?");
42698                             //this.addColumns();
42699                         },
42700                         load : function (_self, records, options)
42701                         {
42702                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42703                                 // if you click on the translation.. you can edit it...
42704                                 var el = Roo.get(this);
42705                                 var id = el.dom.getAttribute('data-id');
42706                                 var d = el.dom.getAttribute('data-date');
42707                                 var t = el.dom.getAttribute('data-time');
42708                                 //var id = this.child('span').dom.textContent;
42709                                 
42710                                 //Roo.log(this);
42711                                 Pman.Dialog.CourseCalendar.show({
42712                                     id : id,
42713                                     when_d : d,
42714                                     when_t : t,
42715                                     productitem_active : id ? 1 : 0
42716                                 }, function() {
42717                                     _this.grid.ds.load({});
42718                                 });
42719                            
42720                            });
42721                            
42722                            _this.panel.fireEvent('resize', [ '', '' ]);
42723                         }
42724                     },
42725                     loadResponse : function(o, success, response){
42726                             // this is overridden on before load..
42727                             
42728                             Roo.log("our code?");       
42729                             //Roo.log(success);
42730                             //Roo.log(response)
42731                             delete this.activeRequest;
42732                             if(!success){
42733                                 this.fireEvent("loadexception", this, o, response);
42734                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42735                                 return;
42736                             }
42737                             var result;
42738                             try {
42739                                 result = o.reader.read(response);
42740                             }catch(e){
42741                                 Roo.log("load exception?");
42742                                 this.fireEvent("loadexception", this, o, response, e);
42743                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42744                                 return;
42745                             }
42746                             Roo.log("ready...");        
42747                             // loop through result.records;
42748                             // and set this.tdate[date] = [] << array of records..
42749                             _this.tdata  = {};
42750                             Roo.each(result.records, function(r){
42751                                 //Roo.log(r.data);
42752                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42753                                     _this.tdata[r.data.when_dt.format('j')] = [];
42754                                 }
42755                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42756                             });
42757                             
42758                             //Roo.log(_this.tdata);
42759                             
42760                             result.records = [];
42761                             result.totalRecords = 6;
42762                     
42763                             // let's generate some duumy records for the rows.
42764                             //var st = _this.dateField.getValue();
42765                             
42766                             // work out monday..
42767                             //st = st.add(Date.DAY, -1 * st.format('w'));
42768                             
42769                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42770                             
42771                             var firstOfMonth = date.getFirstDayOfMonth();
42772                             var days = date.getDaysInMonth();
42773                             var d = 1;
42774                             var firstAdded = false;
42775                             for (var i = 0; i < result.totalRecords ; i++) {
42776                                 //var d= st.add(Date.DAY, i);
42777                                 var row = {};
42778                                 var added = 0;
42779                                 for(var w = 0 ; w < 7 ; w++){
42780                                     if(!firstAdded && firstOfMonth != w){
42781                                         continue;
42782                                     }
42783                                     if(d > days){
42784                                         continue;
42785                                     }
42786                                     firstAdded = true;
42787                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
42788                                     row['weekday'+w] = String.format(
42789                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
42790                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42791                                                     d,
42792                                                     date.format('Y-m-')+dd
42793                                                 );
42794                                     added++;
42795                                     if(typeof(_this.tdata[d]) != 'undefined'){
42796                                         Roo.each(_this.tdata[d], function(r){
42797                                             var is_sub = '';
42798                                             var deactive = '';
42799                                             var id = r.id;
42800                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42801                                             if(r.parent_id*1>0){
42802                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42803                                                 id = r.parent_id;
42804                                             }
42805                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42806                                                 deactive = 'de-act-link';
42807                                             }
42808                                             
42809                                             row['weekday'+w] += String.format(
42810                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42811                                                     id, //0
42812                                                     r.product_id_name, //1
42813                                                     r.when_dt.format('h:ia'), //2
42814                                                     is_sub, //3
42815                                                     deactive, //4
42816                                                     desc // 5
42817                                             );
42818                                         });
42819                                     }
42820                                     d++;
42821                                 }
42822                                 
42823                                 // only do this if something added..
42824                                 if(added > 0){ 
42825                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42826                                 }
42827                                 
42828                                 
42829                                 // push it twice. (second one with an hour..
42830                                 
42831                             }
42832                             //Roo.log(result);
42833                             this.fireEvent("load", this, o, o.request.arg);
42834                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42835                         },
42836                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42837                     proxy : {
42838                         xtype: 'HttpProxy',
42839                         xns: Roo.data,
42840                         method : 'GET',
42841                         url : baseURL + '/Roo/Shop_course.php'
42842                     },
42843                     reader : {
42844                         xtype: 'JsonReader',
42845                         xns: Roo.data,
42846                         id : 'id',
42847                         fields : [
42848                             {
42849                                 'name': 'id',
42850                                 'type': 'int'
42851                             },
42852                             {
42853                                 'name': 'when_dt',
42854                                 'type': 'string'
42855                             },
42856                             {
42857                                 'name': 'end_dt',
42858                                 'type': 'string'
42859                             },
42860                             {
42861                                 'name': 'parent_id',
42862                                 'type': 'int'
42863                             },
42864                             {
42865                                 'name': 'product_id',
42866                                 'type': 'int'
42867                             },
42868                             {
42869                                 'name': 'productitem_id',
42870                                 'type': 'int'
42871                             },
42872                             {
42873                                 'name': 'guid',
42874                                 'type': 'int'
42875                             }
42876                         ]
42877                     }
42878                 },
42879                 toolbar : {
42880                     xtype: 'Toolbar',
42881                     xns: Roo,
42882                     items : [
42883                         {
42884                             xtype: 'Button',
42885                             xns: Roo.Toolbar,
42886                             listeners : {
42887                                 click : function (_self, e)
42888                                 {
42889                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42890                                     sd.setMonth(sd.getMonth()-1);
42891                                     _this.monthField.setValue(sd.format('Y-m-d'));
42892                                     _this.grid.ds.load({});
42893                                 }
42894                             },
42895                             text : "Back"
42896                         },
42897                         {
42898                             xtype: 'Separator',
42899                             xns: Roo.Toolbar
42900                         },
42901                         {
42902                             xtype: 'MonthField',
42903                             xns: Roo.form,
42904                             listeners : {
42905                                 render : function (_self)
42906                                 {
42907                                     _this.monthField = _self;
42908                                    // _this.monthField.set  today
42909                                 },
42910                                 select : function (combo, date)
42911                                 {
42912                                     _this.grid.ds.load({});
42913                                 }
42914                             },
42915                             value : (function() { return new Date(); })()
42916                         },
42917                         {
42918                             xtype: 'Separator',
42919                             xns: Roo.Toolbar
42920                         },
42921                         {
42922                             xtype: 'TextItem',
42923                             xns: Roo.Toolbar,
42924                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42925                         },
42926                         {
42927                             xtype: 'Fill',
42928                             xns: Roo.Toolbar
42929                         },
42930                         {
42931                             xtype: 'Button',
42932                             xns: Roo.Toolbar,
42933                             listeners : {
42934                                 click : function (_self, e)
42935                                 {
42936                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42937                                     sd.setMonth(sd.getMonth()+1);
42938                                     _this.monthField.setValue(sd.format('Y-m-d'));
42939                                     _this.grid.ds.load({});
42940                                 }
42941                             },
42942                             text : "Next"
42943                         }
42944                     ]
42945                 },
42946                  
42947             }
42948         };
42949         
42950         *//*
42951  * Based on:
42952  * Ext JS Library 1.1.1
42953  * Copyright(c) 2006-2007, Ext JS, LLC.
42954  *
42955  * Originally Released Under LGPL - original licence link has changed is not relivant.
42956  *
42957  * Fork - LGPL
42958  * <script type="text/javascript">
42959  */
42960  
42961 /**
42962  * @class Roo.LoadMask
42963  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42964  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42965  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42966  * element's UpdateManager load indicator and will be destroyed after the initial load.
42967  * @constructor
42968  * Create a new LoadMask
42969  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42970  * @param {Object} config The config object
42971  */
42972 Roo.LoadMask = function(el, config){
42973     this.el = Roo.get(el);
42974     Roo.apply(this, config);
42975     if(this.store){
42976         this.store.on('beforeload', this.onBeforeLoad, this);
42977         this.store.on('load', this.onLoad, this);
42978         this.store.on('loadexception', this.onLoadException, this);
42979         this.removeMask = false;
42980     }else{
42981         var um = this.el.getUpdateManager();
42982         um.showLoadIndicator = false; // disable the default indicator
42983         um.on('beforeupdate', this.onBeforeLoad, this);
42984         um.on('update', this.onLoad, this);
42985         um.on('failure', this.onLoad, this);
42986         this.removeMask = true;
42987     }
42988 };
42989
42990 Roo.LoadMask.prototype = {
42991     /**
42992      * @cfg {Boolean} removeMask
42993      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42994      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42995      */
42996     removeMask : false,
42997     /**
42998      * @cfg {String} msg
42999      * The text to display in a centered loading message box (defaults to 'Loading...')
43000      */
43001     msg : 'Loading...',
43002     /**
43003      * @cfg {String} msgCls
43004      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
43005      */
43006     msgCls : 'x-mask-loading',
43007
43008     /**
43009      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
43010      * @type Boolean
43011      */
43012     disabled: false,
43013
43014     /**
43015      * Disables the mask to prevent it from being displayed
43016      */
43017     disable : function(){
43018        this.disabled = true;
43019     },
43020
43021     /**
43022      * Enables the mask so that it can be displayed
43023      */
43024     enable : function(){
43025         this.disabled = false;
43026     },
43027     
43028     onLoadException : function()
43029     {
43030         Roo.log(arguments);
43031         
43032         if (typeof(arguments[3]) != 'undefined') {
43033             Roo.MessageBox.alert("Error loading",arguments[3]);
43034         } 
43035         /*
43036         try {
43037             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43038                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43039             }   
43040         } catch(e) {
43041             
43042         }
43043         */
43044     
43045         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43046     },
43047     // private
43048     onLoad : function()
43049     {
43050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43051     },
43052
43053     // private
43054     onBeforeLoad : function(){
43055         if(!this.disabled){
43056             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43057         }
43058     },
43059
43060     // private
43061     destroy : function(){
43062         if(this.store){
43063             this.store.un('beforeload', this.onBeforeLoad, this);
43064             this.store.un('load', this.onLoad, this);
43065             this.store.un('loadexception', this.onLoadException, this);
43066         }else{
43067             var um = this.el.getUpdateManager();
43068             um.un('beforeupdate', this.onBeforeLoad, this);
43069             um.un('update', this.onLoad, this);
43070             um.un('failure', this.onLoad, this);
43071         }
43072     }
43073 };/*
43074  * Based on:
43075  * Ext JS Library 1.1.1
43076  * Copyright(c) 2006-2007, Ext JS, LLC.
43077  *
43078  * Originally Released Under LGPL - original licence link has changed is not relivant.
43079  *
43080  * Fork - LGPL
43081  * <script type="text/javascript">
43082  */
43083
43084
43085 /**
43086  * @class Roo.XTemplate
43087  * @extends Roo.Template
43088  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43089 <pre><code>
43090 var t = new Roo.XTemplate(
43091         '&lt;select name="{name}"&gt;',
43092                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43093         '&lt;/select&gt;'
43094 );
43095  
43096 // then append, applying the master template values
43097  </code></pre>
43098  *
43099  * Supported features:
43100  *
43101  *  Tags:
43102
43103 <pre><code>
43104       {a_variable} - output encoded.
43105       {a_variable.format:("Y-m-d")} - call a method on the variable
43106       {a_variable:raw} - unencoded output
43107       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43108       {a_variable:this.method_on_template(...)} - call a method on the template object.
43109  
43110 </code></pre>
43111  *  The tpl tag:
43112 <pre><code>
43113         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43114         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43115         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43116         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43117   
43118         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43119         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43120 </code></pre>
43121  *      
43122  */
43123 Roo.XTemplate = function()
43124 {
43125     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43126     if (this.html) {
43127         this.compile();
43128     }
43129 };
43130
43131
43132 Roo.extend(Roo.XTemplate, Roo.Template, {
43133
43134     /**
43135      * The various sub templates
43136      */
43137     tpls : false,
43138     /**
43139      *
43140      * basic tag replacing syntax
43141      * WORD:WORD()
43142      *
43143      * // you can fake an object call by doing this
43144      *  x.t:(test,tesT) 
43145      * 
43146      */
43147     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43148
43149     /**
43150      * compile the template
43151      *
43152      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43153      *
43154      */
43155     compile: function()
43156     {
43157         var s = this.html;
43158      
43159         s = ['<tpl>', s, '</tpl>'].join('');
43160     
43161         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43162             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43163             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43164             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43165             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43166             m,
43167             id     = 0,
43168             tpls   = [];
43169     
43170         while(true == !!(m = s.match(re))){
43171             var forMatch   = m[0].match(nameRe),
43172                 ifMatch   = m[0].match(ifRe),
43173                 execMatch   = m[0].match(execRe),
43174                 namedMatch   = m[0].match(namedRe),
43175                 
43176                 exp  = null, 
43177                 fn   = null,
43178                 exec = null,
43179                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43180                 
43181             if (ifMatch) {
43182                 // if - puts fn into test..
43183                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43184                 if(exp){
43185                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43186                 }
43187             }
43188             
43189             if (execMatch) {
43190                 // exec - calls a function... returns empty if true is  returned.
43191                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43192                 if(exp){
43193                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43194                 }
43195             }
43196             
43197             
43198             if (name) {
43199                 // for = 
43200                 switch(name){
43201                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43202                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43203                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43204                 }
43205             }
43206             var uid = namedMatch ? namedMatch[1] : id;
43207             
43208             
43209             tpls.push({
43210                 id:     namedMatch ? namedMatch[1] : id,
43211                 target: name,
43212                 exec:   exec,
43213                 test:   fn,
43214                 body:   m[1] || ''
43215             });
43216             if (namedMatch) {
43217                 s = s.replace(m[0], '');
43218             } else { 
43219                 s = s.replace(m[0], '{xtpl'+ id + '}');
43220             }
43221             ++id;
43222         }
43223         this.tpls = [];
43224         for(var i = tpls.length-1; i >= 0; --i){
43225             this.compileTpl(tpls[i]);
43226             this.tpls[tpls[i].id] = tpls[i];
43227         }
43228         this.master = tpls[tpls.length-1];
43229         return this;
43230     },
43231     /**
43232      * same as applyTemplate, except it's done to one of the subTemplates
43233      * when using named templates, you can do:
43234      *
43235      * var str = pl.applySubTemplate('your-name', values);
43236      *
43237      * 
43238      * @param {Number} id of the template
43239      * @param {Object} values to apply to template
43240      * @param {Object} parent (normaly the instance of this object)
43241      */
43242     applySubTemplate : function(id, values, parent)
43243     {
43244         
43245         
43246         var t = this.tpls[id];
43247         
43248         
43249         try { 
43250             if(t.test && !t.test.call(this, values, parent)){
43251                 return '';
43252             }
43253         } catch(e) {
43254             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43255             Roo.log(e.toString());
43256             Roo.log(t.test);
43257             return ''
43258         }
43259         try { 
43260             
43261             if(t.exec && t.exec.call(this, values, parent)){
43262                 return '';
43263             }
43264         } catch(e) {
43265             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43266             Roo.log(e.toString());
43267             Roo.log(t.exec);
43268             return ''
43269         }
43270         try {
43271             var vs = t.target ? t.target.call(this, values, parent) : values;
43272             parent = t.target ? values : parent;
43273             if(t.target && vs instanceof Array){
43274                 var buf = [];
43275                 for(var i = 0, len = vs.length; i < len; i++){
43276                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43277                 }
43278                 return buf.join('');
43279             }
43280             return t.compiled.call(this, vs, parent);
43281         } catch (e) {
43282             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43283             Roo.log(e.toString());
43284             Roo.log(t.compiled);
43285             return '';
43286         }
43287     },
43288
43289     compileTpl : function(tpl)
43290     {
43291         var fm = Roo.util.Format;
43292         var useF = this.disableFormats !== true;
43293         var sep = Roo.isGecko ? "+" : ",";
43294         var undef = function(str) {
43295             Roo.log("Property not found :"  + str);
43296             return '';
43297         };
43298         
43299         var fn = function(m, name, format, args)
43300         {
43301             //Roo.log(arguments);
43302             args = args ? args.replace(/\\'/g,"'") : args;
43303             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43304             if (typeof(format) == 'undefined') {
43305                 format= 'htmlEncode';
43306             }
43307             if (format == 'raw' ) {
43308                 format = false;
43309             }
43310             
43311             if(name.substr(0, 4) == 'xtpl'){
43312                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43313             }
43314             
43315             // build an array of options to determine if value is undefined..
43316             
43317             // basically get 'xxxx.yyyy' then do
43318             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43319             //    (function () { Roo.log("Property not found"); return ''; })() :
43320             //    ......
43321             
43322             var udef_ar = [];
43323             var lookfor = '';
43324             Roo.each(name.split('.'), function(st) {
43325                 lookfor += (lookfor.length ? '.': '') + st;
43326                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43327             });
43328             
43329             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43330             
43331             
43332             if(format && useF){
43333                 
43334                 args = args ? ',' + args : "";
43335                  
43336                 if(format.substr(0, 5) != "this."){
43337                     format = "fm." + format + '(';
43338                 }else{
43339                     format = 'this.call("'+ format.substr(5) + '", ';
43340                     args = ", values";
43341                 }
43342                 
43343                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43344             }
43345              
43346             if (args.length) {
43347                 // called with xxyx.yuu:(test,test)
43348                 // change to ()
43349                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43350             }
43351             // raw.. - :raw modifier..
43352             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43353             
43354         };
43355         var body;
43356         // branched to use + in gecko and [].join() in others
43357         if(Roo.isGecko){
43358             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43359                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43360                     "';};};";
43361         }else{
43362             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43363             body.push(tpl.body.replace(/(\r\n|\n)/g,
43364                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43365             body.push("'].join('');};};");
43366             body = body.join('');
43367         }
43368         
43369         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43370        
43371         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43372         eval(body);
43373         
43374         return this;
43375     },
43376
43377     applyTemplate : function(values){
43378         return this.master.compiled.call(this, values, {});
43379         //var s = this.subs;
43380     },
43381
43382     apply : function(){
43383         return this.applyTemplate.apply(this, arguments);
43384     }
43385
43386  });
43387
43388 Roo.XTemplate.from = function(el){
43389     el = Roo.getDom(el);
43390     return new Roo.XTemplate(el.value || el.innerHTML);
43391 };