cssX/roojs-all.css
[roojs1] / roojs-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
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358
359         /**
360          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
361          * @param {String} string
362          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
363          * @return {Object} A literal with members
364          */
365         urlDecode : function(string, overwrite){
366             if(!string || !string.length){
367                 return {};
368             }
369             var obj = {};
370             var pairs = string.split('&');
371             var pair, name, value;
372             for(var i = 0, len = pairs.length; i < len; i++){
373                 pair = pairs[i].split('=');
374                 name = decodeURIComponent(pair[0]);
375                 value = decodeURIComponent(pair[1]);
376                 if(overwrite !== true){
377                     if(typeof obj[name] == "undefined"){
378                         obj[name] = value;
379                     }else if(typeof obj[name] == "string"){
380                         obj[name] = [obj[name]];
381                         obj[name].push(value);
382                     }else{
383                         obj[name].push(value);
384                     }
385                 }else{
386                     obj[name] = value;
387                 }
388             }
389             return obj;
390         },
391
392         /**
393          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
394          * passed array is not really an array, your function is called once with it.
395          * The supplied function is called with (Object item, Number index, Array allItems).
396          * @param {Array/NodeList/Mixed} array
397          * @param {Function} fn
398          * @param {Object} scope
399          */
400         each : function(array, fn, scope){
401             if(typeof array.length == "undefined" || typeof array == "string"){
402                 array = [array];
403             }
404             for(var i = 0, len = array.length; i < len; i++){
405                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
406             }
407         },
408
409         // deprecated
410         combine : function(){
411             var as = arguments, l = as.length, r = [];
412             for(var i = 0; i < l; i++){
413                 var a = as[i];
414                 if(a instanceof Array){
415                     r = r.concat(a);
416                 }else if(a.length !== undefined && !a.substr){
417                     r = r.concat(Array.prototype.slice.call(a, 0));
418                 }else{
419                     r.push(a);
420                 }
421             }
422             return r;
423         },
424
425         /**
426          * Escapes the passed string for use in a regular expression
427          * @param {String} str
428          * @return {String}
429          */
430         escapeRe : function(s) {
431             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
432         },
433
434         // internal
435         callback : function(cb, scope, args, delay){
436             if(typeof cb == "function"){
437                 if(delay){
438                     cb.defer(delay, scope, args || []);
439                 }else{
440                     cb.apply(scope, args || []);
441                 }
442             }
443         },
444
445         /**
446          * Return the dom node for the passed string (id), dom node, or Roo.Element
447          * @param {String/HTMLElement/Roo.Element} el
448          * @return HTMLElement
449          */
450         getDom : function(el){
451             if(!el){
452                 return null;
453             }
454             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
455         },
456
457         /**
458         * Shorthand for {@link Roo.ComponentMgr#get}
459         * @param {String} id
460         * @return Roo.Component
461         */
462         getCmp : function(id){
463             return Roo.ComponentMgr.get(id);
464         },
465          
466         num : function(v, defaultValue){
467             if(typeof v != 'number'){
468                 return defaultValue;
469             }
470             return v;
471         },
472
473         destroy : function(){
474             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
475                 var as = a[i];
476                 if(as){
477                     if(as.dom){
478                         as.removeAllListeners();
479                         as.remove();
480                         continue;
481                     }
482                     if(typeof as.purgeListeners == 'function'){
483                         as.purgeListeners();
484                     }
485                     if(typeof as.destroy == 'function'){
486                         as.destroy();
487                     }
488                 }
489             }
490         },
491
492         // inpired by a similar function in mootools library
493         /**
494          * Returns the type of object that is passed in. If the object passed in is null or undefined it
495          * return false otherwise it returns one of the following values:<ul>
496          * <li><b>string</b>: If the object passed is a string</li>
497          * <li><b>number</b>: If the object passed is a number</li>
498          * <li><b>boolean</b>: If the object passed is a boolean value</li>
499          * <li><b>function</b>: If the object passed is a function reference</li>
500          * <li><b>object</b>: If the object passed is an object</li>
501          * <li><b>array</b>: If the object passed is an array</li>
502          * <li><b>regexp</b>: If the object passed is a regular expression</li>
503          * <li><b>element</b>: If the object passed is a DOM Element</li>
504          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
505          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
506          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
507          * @param {Mixed} object
508          * @return {String}
509          */
510         type : function(o){
511             if(o === undefined || o === null){
512                 return false;
513             }
514             if(o.htmlElement){
515                 return 'element';
516             }
517             var t = typeof o;
518             if(t == 'object' && o.nodeName) {
519                 switch(o.nodeType) {
520                     case 1: return 'element';
521                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
522                 }
523             }
524             if(t == 'object' || t == 'function') {
525                 switch(o.constructor) {
526                     case Array: return 'array';
527                     case RegExp: return 'regexp';
528                 }
529                 if(typeof o.length == 'number' && typeof o.item == 'function') {
530                     return 'nodelist';
531                 }
532             }
533             return t;
534         },
535
536         /**
537          * Returns true if the passed value is null, undefined or an empty string (optional).
538          * @param {Mixed} value The value to test
539          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
540          * @return {Boolean}
541          */
542         isEmpty : function(v, allowBlank){
543             return v === null || v === undefined || (!allowBlank ? v === '' : false);
544         },
545         
546         /** @type Boolean */
547         isOpera : isOpera,
548         /** @type Boolean */
549         isSafari : isSafari,
550         /** @type Boolean */
551         isIE : isIE,
552         /** @type Boolean */
553         isIE7 : isIE7,
554         /** @type Boolean */
555         isGecko : isGecko,
556         /** @type Boolean */
557         isBorderBox : isBorderBox,
558         /** @type Boolean */
559         isWindows : isWindows,
560         /** @type Boolean */
561         isLinux : isLinux,
562         /** @type Boolean */
563         isMac : isMac,
564
565         /**
566          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
567          * you may want to set this to true.
568          * @type Boolean
569          */
570         useShims : ((isIE && !isIE7) || (isGecko && isMac))
571     });
572
573
574 })();
575
576 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
577                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
578 /*
579  * Based on:
580  * Ext JS Library 1.1.1
581  * Copyright(c) 2006-2007, Ext JS, LLC.
582  *
583  * Originally Released Under LGPL - original licence link has changed is not relivant.
584  *
585  * Fork - LGPL
586  * <script type="text/javascript">
587  */
588
589 (function() {    
590     // wrappedn so fnCleanup is not in global scope...
591     if(Roo.isIE) {
592         function fnCleanUp() {
593             var p = Function.prototype;
594             delete p.createSequence;
595             delete p.defer;
596             delete p.createDelegate;
597             delete p.createCallback;
598             delete p.createInterceptor;
599
600             window.detachEvent("onunload", fnCleanUp);
601         }
602         window.attachEvent("onunload", fnCleanUp);
603     }
604 })();
605
606
607 /**
608  * @class Function
609  * These functions are available on every Function object (any JavaScript function).
610  */
611 Roo.apply(Function.prototype, {
612      /**
613      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
614      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
615      * Will create a function that is bound to those 2 args.
616      * @return {Function} The new function
617     */
618     createCallback : function(/*args...*/){
619         // make args available, in function below
620         var args = arguments;
621         var method = this;
622         return function() {
623             return method.apply(window, args);
624         };
625     },
626
627     /**
628      * Creates a delegate (callback) that sets the scope to obj.
629      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
630      * Will create a function that is automatically scoped to this.
631      * @param {Object} obj (optional) The object for which the scope is set
632      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
633      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
634      *                                             if a number the args are inserted at the specified position
635      * @return {Function} The new function
636      */
637     createDelegate : function(obj, args, appendArgs){
638         var method = this;
639         return function() {
640             var callArgs = args || arguments;
641             if(appendArgs === true){
642                 callArgs = Array.prototype.slice.call(arguments, 0);
643                 callArgs = callArgs.concat(args);
644             }else if(typeof appendArgs == "number"){
645                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
646                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
647                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
648             }
649             return method.apply(obj || window, callArgs);
650         };
651     },
652
653     /**
654      * Calls this function after the number of millseconds specified.
655      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
656      * @param {Object} obj (optional) The object for which the scope is set
657      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
658      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
659      *                                             if a number the args are inserted at the specified position
660      * @return {Number} The timeout id that can be used with clearTimeout
661      */
662     defer : function(millis, obj, args, appendArgs){
663         var fn = this.createDelegate(obj, args, appendArgs);
664         if(millis){
665             return setTimeout(fn, millis);
666         }
667         fn();
668         return 0;
669     },
670     /**
671      * Create a combined function call sequence of the original function + the passed function.
672      * The resulting function returns the results of the original function.
673      * The passed fcn is called with the parameters of the original function
674      * @param {Function} fcn The function to sequence
675      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
676      * @return {Function} The new function
677      */
678     createSequence : function(fcn, scope){
679         if(typeof fcn != "function"){
680             return this;
681         }
682         var method = this;
683         return function() {
684             var retval = method.apply(this || window, arguments);
685             fcn.apply(scope || this || window, arguments);
686             return retval;
687         };
688     },
689
690     /**
691      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
692      * The resulting function returns the results of the original function.
693      * The passed fcn is called with the parameters of the original function.
694      * @addon
695      * @param {Function} fcn The function to call before the original
696      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
697      * @return {Function} The new function
698      */
699     createInterceptor : function(fcn, scope){
700         if(typeof fcn != "function"){
701             return this;
702         }
703         var method = this;
704         return function() {
705             fcn.target = this;
706             fcn.method = method;
707             if(fcn.apply(scope || this || window, arguments) === false){
708                 return;
709             }
710             return method.apply(this || window, arguments);
711         };
712     }
713 });
714 /*
715  * Based on:
716  * Ext JS Library 1.1.1
717  * Copyright(c) 2006-2007, Ext JS, LLC.
718  *
719  * Originally Released Under LGPL - original licence link has changed is not relivant.
720  *
721  * Fork - LGPL
722  * <script type="text/javascript">
723  */
724
725 Roo.applyIf(String, {
726     
727     /** @scope String */
728     
729     /**
730      * Escapes the passed string for ' and \
731      * @param {String} string The string to escape
732      * @return {String} The escaped string
733      * @static
734      */
735     escape : function(string) {
736         return string.replace(/('|\\)/g, "\\$1");
737     },
738
739     /**
740      * Pads the left side of a string with a specified character.  This is especially useful
741      * for normalizing number and date strings.  Example usage:
742      * <pre><code>
743 var s = String.leftPad('123', 5, '0');
744 // s now contains the string: '00123'
745 </code></pre>
746      * @param {String} string The original string
747      * @param {Number} size The total length of the output string
748      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
749      * @return {String} The padded string
750      * @static
751      */
752     leftPad : function (val, size, ch) {
753         var result = new String(val);
754         if(ch === null || ch === undefined || ch === '') {
755             ch = " ";
756         }
757         while (result.length < size) {
758             result = ch + result;
759         }
760         return result;
761     },
762
763     /**
764      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
765      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
766      * <pre><code>
767 var cls = 'my-class', text = 'Some text';
768 var s = String.format('<div class="{0}">{1}</div>', cls, text);
769 // s now contains the string: '<div class="my-class">Some text</div>'
770 </code></pre>
771      * @param {String} string The tokenized string to be formatted
772      * @param {String} value1 The value to replace token {0}
773      * @param {String} value2 Etc...
774      * @return {String} The formatted string
775      * @static
776      */
777     format : function(format){
778         var args = Array.prototype.slice.call(arguments, 1);
779         return format.replace(/\{(\d+)\}/g, function(m, i){
780             return Roo.util.Format.htmlEncode(args[i]);
781         });
782     }
783 });
784
785 /**
786  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
787  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
788  * they are already different, the first value passed in is returned.  Note that this method returns the new value
789  * but does not change the current string.
790  * <pre><code>
791 // alternate sort directions
792 sort = sort.toggle('ASC', 'DESC');
793
794 // instead of conditional logic:
795 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
796 </code></pre>
797  * @param {String} value The value to compare to the current string
798  * @param {String} other The new value to use if the string already equals the first value passed in
799  * @return {String} The new value
800  */
801  
802 String.prototype.toggle = function(value, other){
803     return this == value ? other : value;
804 };/*
805  * Based on:
806  * Ext JS Library 1.1.1
807  * Copyright(c) 2006-2007, Ext JS, LLC.
808  *
809  * Originally Released Under LGPL - original licence link has changed is not relivant.
810  *
811  * Fork - LGPL
812  * <script type="text/javascript">
813  */
814
815  /**
816  * @class Number
817  */
818 Roo.applyIf(Number.prototype, {
819     /**
820      * Checks whether or not the current number is within a desired range.  If the number is already within the
821      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
822      * exceeded.  Note that this method returns the constrained value but does not change the current number.
823      * @param {Number} min The minimum number in the range
824      * @param {Number} max The maximum number in the range
825      * @return {Number} The constrained value if outside the range, otherwise the current value
826      */
827     constrain : function(min, max){
828         return Math.min(Math.max(this, min), max);
829     }
830 });/*
831  * Based on:
832  * Ext JS Library 1.1.1
833  * Copyright(c) 2006-2007, Ext JS, LLC.
834  *
835  * Originally Released Under LGPL - original licence link has changed is not relivant.
836  *
837  * Fork - LGPL
838  * <script type="text/javascript">
839  */
840  /**
841  * @class Array
842  */
843 Roo.applyIf(Array.prototype, {
844     /**
845      * Checks whether or not the specified object exists in the array.
846      * @param {Object} o The object to check for
847      * @return {Number} The index of o in the array (or -1 if it is not found)
848      */
849     indexOf : function(o){
850        for (var i = 0, len = this.length; i < len; i++){
851               if(this[i] == o) return i;
852        }
853            return -1;
854     },
855
856     /**
857      * Removes the specified object from the array.  If the object is not found nothing happens.
858      * @param {Object} o The object to remove
859      */
860     remove : function(o){
861        var index = this.indexOf(o);
862        if(index != -1){
863            this.splice(index, 1);
864        }
865     },
866     /**
867      * Map (JS 1.6 compatibility)
868      * @param {Function} function  to call
869      */
870     map : function(fun )
871     {
872         var len = this.length >>> 0;
873         if (typeof fun != "function")
874             throw new TypeError();
875
876         var res = new Array(len);
877         var thisp = arguments[1];
878         for (var i = 0; i < len; i++)
879         {
880             if (i in this)
881                 res[i] = fun.call(thisp, this[i], i, this);
882         }
883
884         return res;
885     }
886     
887 });
888
889
890  /*
891  * Based on:
892  * Ext JS Library 1.1.1
893  * Copyright(c) 2006-2007, Ext JS, LLC.
894  *
895  * Originally Released Under LGPL - original licence link has changed is not relivant.
896  *
897  * Fork - LGPL
898  * <script type="text/javascript">
899  */
900
901 /**
902  * @class Date
903  *
904  * The date parsing and format syntax is a subset of
905  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
906  * supported will provide results equivalent to their PHP versions.
907  *
908  * Following is the list of all currently supported formats:
909  *<pre>
910 Sample date:
911 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
912
913 Format  Output      Description
914 ------  ----------  --------------------------------------------------------------
915   d      10         Day of the month, 2 digits with leading zeros
916   D      Wed        A textual representation of a day, three letters
917   j      10         Day of the month without leading zeros
918   l      Wednesday  A full textual representation of the day of the week
919   S      th         English ordinal day of month suffix, 2 chars (use with j)
920   w      3          Numeric representation of the day of the week
921   z      9          The julian date, or day of the year (0-365)
922   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
923   F      January    A full textual representation of the month
924   m      01         Numeric representation of a month, with leading zeros
925   M      Jan        Month name abbreviation, three letters
926   n      1          Numeric representation of a month, without leading zeros
927   t      31         Number of days in the given month
928   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
929   Y      2007       A full numeric representation of a year, 4 digits
930   y      07         A two digit representation of a year
931   a      pm         Lowercase Ante meridiem and Post meridiem
932   A      PM         Uppercase Ante meridiem and Post meridiem
933   g      3          12-hour format of an hour without leading zeros
934   G      15         24-hour format of an hour without leading zeros
935   h      03         12-hour format of an hour with leading zeros
936   H      15         24-hour format of an hour with leading zeros
937   i      05         Minutes with leading zeros
938   s      01         Seconds, with leading zeros
939   O      -0600      Difference to Greenwich time (GMT) in hours
940   T      CST        Timezone setting of the machine running the code
941   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
942 </pre>
943  *
944  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
945  * <pre><code>
946 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
947 document.write(dt.format('Y-m-d'));                         //2007-01-10
948 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
949 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
950  </code></pre>
951  *
952  * Here are some standard date/time patterns that you might find helpful.  They
953  * are not part of the source of Date.js, but to use them you can simply copy this
954  * block of code into any script that is included after Date.js and they will also become
955  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
956  * <pre><code>
957 Date.patterns = {
958     ISO8601Long:"Y-m-d H:i:s",
959     ISO8601Short:"Y-m-d",
960     ShortDate: "n/j/Y",
961     LongDate: "l, F d, Y",
962     FullDateTime: "l, F d, Y g:i:s A",
963     MonthDay: "F d",
964     ShortTime: "g:i A",
965     LongTime: "g:i:s A",
966     SortableDateTime: "Y-m-d\\TH:i:s",
967     UniversalSortableDateTime: "Y-m-d H:i:sO",
968     YearMonth: "F, Y"
969 };
970 </code></pre>
971  *
972  * Example usage:
973  * <pre><code>
974 var dt = new Date();
975 document.write(dt.format(Date.patterns.ShortDate));
976  </code></pre>
977  */
978
979 /*
980  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
981  * They generate precompiled functions from date formats instead of parsing and
982  * processing the pattern every time you format a date.  These functions are available
983  * on every Date object (any javascript function).
984  *
985  * The original article and download are here:
986  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
987  *
988  */
989  
990  
991  // was in core
992 /**
993  Returns the number of milliseconds between this date and date
994  @param {Date} date (optional) Defaults to now
995  @return {Number} The diff in milliseconds
996  @member Date getElapsed
997  */
998 Date.prototype.getElapsed = function(date) {
999         return Math.abs((date || new Date()).getTime()-this.getTime());
1000 };
1001 // was in date file..
1002
1003
1004 // private
1005 Date.parseFunctions = {count:0};
1006 // private
1007 Date.parseRegexes = [];
1008 // private
1009 Date.formatFunctions = {count:0};
1010
1011 // private
1012 Date.prototype.dateFormat = function(format) {
1013     if (Date.formatFunctions[format] == null) {
1014         Date.createNewFormat(format);
1015     }
1016     var func = Date.formatFunctions[format];
1017     return this[func]();
1018 };
1019
1020
1021 /**
1022  * Formats a date given the supplied format string
1023  * @param {String} format The format string
1024  * @return {String} The formatted date
1025  * @method
1026  */
1027 Date.prototype.format = Date.prototype.dateFormat;
1028
1029 // private
1030 Date.createNewFormat = function(format) {
1031     var funcName = "format" + Date.formatFunctions.count++;
1032     Date.formatFunctions[format] = funcName;
1033     var code = "Date.prototype." + funcName + " = function(){return ";
1034     var special = false;
1035     var ch = '';
1036     for (var i = 0; i < format.length; ++i) {
1037         ch = format.charAt(i);
1038         if (!special && ch == "\\") {
1039             special = true;
1040         }
1041         else if (special) {
1042             special = false;
1043             code += "'" + String.escape(ch) + "' + ";
1044         }
1045         else {
1046             code += Date.getFormatCode(ch);
1047         }
1048     }
1049     /** eval:var:zzzzzzzzzzzzz */
1050     eval(code.substring(0, code.length - 3) + ";}");
1051 };
1052
1053 // private
1054 Date.getFormatCode = function(character) {
1055     switch (character) {
1056     case "d":
1057         return "String.leftPad(this.getDate(), 2, '0') + ";
1058     case "D":
1059         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1060     case "j":
1061         return "this.getDate() + ";
1062     case "l":
1063         return "Date.dayNames[this.getDay()] + ";
1064     case "S":
1065         return "this.getSuffix() + ";
1066     case "w":
1067         return "this.getDay() + ";
1068     case "z":
1069         return "this.getDayOfYear() + ";
1070     case "W":
1071         return "this.getWeekOfYear() + ";
1072     case "F":
1073         return "Date.monthNames[this.getMonth()] + ";
1074     case "m":
1075         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1076     case "M":
1077         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1078     case "n":
1079         return "(this.getMonth() + 1) + ";
1080     case "t":
1081         return "this.getDaysInMonth() + ";
1082     case "L":
1083         return "(this.isLeapYear() ? 1 : 0) + ";
1084     case "Y":
1085         return "this.getFullYear() + ";
1086     case "y":
1087         return "('' + this.getFullYear()).substring(2, 4) + ";
1088     case "a":
1089         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1090     case "A":
1091         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1092     case "g":
1093         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1094     case "G":
1095         return "this.getHours() + ";
1096     case "h":
1097         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1098     case "H":
1099         return "String.leftPad(this.getHours(), 2, '0') + ";
1100     case "i":
1101         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1102     case "s":
1103         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1104     case "O":
1105         return "this.getGMTOffset() + ";
1106     case "T":
1107         return "this.getTimezone() + ";
1108     case "Z":
1109         return "(this.getTimezoneOffset() * -60) + ";
1110     default:
1111         return "'" + String.escape(character) + "' + ";
1112     }
1113 };
1114
1115 /**
1116  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1117  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1118  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1119  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1120  * string or the parse operation will fail.
1121  * Example Usage:
1122 <pre><code>
1123 //dt = Fri May 25 2007 (current date)
1124 var dt = new Date();
1125
1126 //dt = Thu May 25 2006 (today's month/day in 2006)
1127 dt = Date.parseDate("2006", "Y");
1128
1129 //dt = Sun Jan 15 2006 (all date parts specified)
1130 dt = Date.parseDate("2006-1-15", "Y-m-d");
1131
1132 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1133 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1134 </code></pre>
1135  * @param {String} input The unparsed date as a string
1136  * @param {String} format The format the date is in
1137  * @return {Date} The parsed date
1138  * @static
1139  */
1140 Date.parseDate = function(input, format) {
1141     if (Date.parseFunctions[format] == null) {
1142         Date.createParser(format);
1143     }
1144     var func = Date.parseFunctions[format];
1145     return Date[func](input);
1146 };
1147 /**
1148  * @private
1149  */
1150 Date.createParser = function(format) {
1151     var funcName = "parse" + Date.parseFunctions.count++;
1152     var regexNum = Date.parseRegexes.length;
1153     var currentGroup = 1;
1154     Date.parseFunctions[format] = funcName;
1155
1156     var code = "Date." + funcName + " = function(input){\n"
1157         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1158         + "var d = new Date();\n"
1159         + "y = d.getFullYear();\n"
1160         + "m = d.getMonth();\n"
1161         + "d = d.getDate();\n"
1162         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1163         + "if (results && results.length > 0) {";
1164     var regex = "";
1165
1166     var special = false;
1167     var ch = '';
1168     for (var i = 0; i < format.length; ++i) {
1169         ch = format.charAt(i);
1170         if (!special && ch == "\\") {
1171             special = true;
1172         }
1173         else if (special) {
1174             special = false;
1175             regex += String.escape(ch);
1176         }
1177         else {
1178             var obj = Date.formatCodeToRegex(ch, currentGroup);
1179             currentGroup += obj.g;
1180             regex += obj.s;
1181             if (obj.g && obj.c) {
1182                 code += obj.c;
1183             }
1184         }
1185     }
1186
1187     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1188         + "{v = new Date(y, m, d, h, i, s);}\n"
1189         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1190         + "{v = new Date(y, m, d, h, i);}\n"
1191         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1192         + "{v = new Date(y, m, d, h);}\n"
1193         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1194         + "{v = new Date(y, m, d);}\n"
1195         + "else if (y >= 0 && m >= 0)\n"
1196         + "{v = new Date(y, m);}\n"
1197         + "else if (y >= 0)\n"
1198         + "{v = new Date(y);}\n"
1199         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1200         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1201         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1202         + ";}";
1203
1204     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1205     /** eval:var:zzzzzzzzzzzzz */
1206     eval(code);
1207 };
1208
1209 // private
1210 Date.formatCodeToRegex = function(character, currentGroup) {
1211     switch (character) {
1212     case "D":
1213         return {g:0,
1214         c:null,
1215         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1216     case "j":
1217         return {g:1,
1218             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1219             s:"(\\d{1,2})"}; // day of month without leading zeroes
1220     case "d":
1221         return {g:1,
1222             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1223             s:"(\\d{2})"}; // day of month with leading zeroes
1224     case "l":
1225         return {g:0,
1226             c:null,
1227             s:"(?:" + Date.dayNames.join("|") + ")"};
1228     case "S":
1229         return {g:0,
1230             c:null,
1231             s:"(?:st|nd|rd|th)"};
1232     case "w":
1233         return {g:0,
1234             c:null,
1235             s:"\\d"};
1236     case "z":
1237         return {g:0,
1238             c:null,
1239             s:"(?:\\d{1,3})"};
1240     case "W":
1241         return {g:0,
1242             c:null,
1243             s:"(?:\\d{2})"};
1244     case "F":
1245         return {g:1,
1246             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1247             s:"(" + Date.monthNames.join("|") + ")"};
1248     case "M":
1249         return {g:1,
1250             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1251             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1252     case "n":
1253         return {g:1,
1254             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1255             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1256     case "m":
1257         return {g:1,
1258             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1259             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1260     case "t":
1261         return {g:0,
1262             c:null,
1263             s:"\\d{1,2}"};
1264     case "L":
1265         return {g:0,
1266             c:null,
1267             s:"(?:1|0)"};
1268     case "Y":
1269         return {g:1,
1270             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1271             s:"(\\d{4})"};
1272     case "y":
1273         return {g:1,
1274             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1275                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1276             s:"(\\d{1,2})"};
1277     case "a":
1278         return {g:1,
1279             c:"if (results[" + currentGroup + "] == 'am') {\n"
1280                 + "if (h == 12) { h = 0; }\n"
1281                 + "} else { if (h < 12) { h += 12; }}",
1282             s:"(am|pm)"};
1283     case "A":
1284         return {g:1,
1285             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1286                 + "if (h == 12) { h = 0; }\n"
1287                 + "} else { if (h < 12) { h += 12; }}",
1288             s:"(AM|PM)"};
1289     case "g":
1290     case "G":
1291         return {g:1,
1292             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1293             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1294     case "h":
1295     case "H":
1296         return {g:1,
1297             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1299     case "i":
1300         return {g:1,
1301             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1302             s:"(\\d{2})"};
1303     case "s":
1304         return {g:1,
1305             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1306             s:"(\\d{2})"};
1307     case "O":
1308         return {g:1,
1309             c:[
1310                 "o = results[", currentGroup, "];\n",
1311                 "var sn = o.substring(0,1);\n", // get + / - sign
1312                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1313                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1314                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1315                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1316             ].join(""),
1317             s:"([+\-]\\d{4})"};
1318     case "T":
1319         return {g:0,
1320             c:null,
1321             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1322     case "Z":
1323         return {g:1,
1324             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1325                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1326             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1327     default:
1328         return {g:0,
1329             c:null,
1330             s:String.escape(character)};
1331     }
1332 };
1333
1334 /**
1335  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1336  * @return {String} The abbreviated timezone name (e.g. 'CST')
1337  */
1338 Date.prototype.getTimezone = function() {
1339     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1340 };
1341
1342 /**
1343  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1344  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1345  */
1346 Date.prototype.getGMTOffset = function() {
1347     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1348         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1349         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1350 };
1351
1352 /**
1353  * Get the numeric day number of the year, adjusted for leap year.
1354  * @return {Number} 0 through 364 (365 in leap years)
1355  */
1356 Date.prototype.getDayOfYear = function() {
1357     var num = 0;
1358     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1359     for (var i = 0; i < this.getMonth(); ++i) {
1360         num += Date.daysInMonth[i];
1361     }
1362     return num + this.getDate() - 1;
1363 };
1364
1365 /**
1366  * Get the string representation of the numeric week number of the year
1367  * (equivalent to the format specifier 'W').
1368  * @return {String} '00' through '52'
1369  */
1370 Date.prototype.getWeekOfYear = function() {
1371     // Skip to Thursday of this week
1372     var now = this.getDayOfYear() + (4 - this.getDay());
1373     // Find the first Thursday of the year
1374     var jan1 = new Date(this.getFullYear(), 0, 1);
1375     var then = (7 - jan1.getDay() + 4);
1376     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1377 };
1378
1379 /**
1380  * Whether or not the current date is in a leap year.
1381  * @return {Boolean} True if the current date is in a leap year, else false
1382  */
1383 Date.prototype.isLeapYear = function() {
1384     var year = this.getFullYear();
1385     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1386 };
1387
1388 /**
1389  * Get the first day of the current month, adjusted for leap year.  The returned value
1390  * is the numeric day index within the week (0-6) which can be used in conjunction with
1391  * the {@link #monthNames} array to retrieve the textual day name.
1392  * Example:
1393  *<pre><code>
1394 var dt = new Date('1/10/2007');
1395 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1396 </code></pre>
1397  * @return {Number} The day number (0-6)
1398  */
1399 Date.prototype.getFirstDayOfMonth = function() {
1400     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1401     return (day < 0) ? (day + 7) : day;
1402 };
1403
1404 /**
1405  * Get the last day of the current month, adjusted for leap year.  The returned value
1406  * is the numeric day index within the week (0-6) which can be used in conjunction with
1407  * the {@link #monthNames} array to retrieve the textual day name.
1408  * Example:
1409  *<pre><code>
1410 var dt = new Date('1/10/2007');
1411 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1412 </code></pre>
1413  * @return {Number} The day number (0-6)
1414  */
1415 Date.prototype.getLastDayOfMonth = function() {
1416     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1417     return (day < 0) ? (day + 7) : day;
1418 };
1419
1420
1421 /**
1422  * Get the first date of this date's month
1423  * @return {Date}
1424  */
1425 Date.prototype.getFirstDateOfMonth = function() {
1426     return new Date(this.getFullYear(), this.getMonth(), 1);
1427 };
1428
1429 /**
1430  * Get the last date of this date's month
1431  * @return {Date}
1432  */
1433 Date.prototype.getLastDateOfMonth = function() {
1434     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1435 };
1436 /**
1437  * Get the number of days in the current month, adjusted for leap year.
1438  * @return {Number} The number of days in the month
1439  */
1440 Date.prototype.getDaysInMonth = function() {
1441     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1442     return Date.daysInMonth[this.getMonth()];
1443 };
1444
1445 /**
1446  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1447  * @return {String} 'st, 'nd', 'rd' or 'th'
1448  */
1449 Date.prototype.getSuffix = function() {
1450     switch (this.getDate()) {
1451         case 1:
1452         case 21:
1453         case 31:
1454             return "st";
1455         case 2:
1456         case 22:
1457             return "nd";
1458         case 3:
1459         case 23:
1460             return "rd";
1461         default:
1462             return "th";
1463     }
1464 };
1465
1466 // private
1467 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1468
1469 /**
1470  * An array of textual month names.
1471  * Override these values for international dates, for example...
1472  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1473  * @type Array
1474  * @static
1475  */
1476 Date.monthNames =
1477    ["January",
1478     "February",
1479     "March",
1480     "April",
1481     "May",
1482     "June",
1483     "July",
1484     "August",
1485     "September",
1486     "October",
1487     "November",
1488     "December"];
1489
1490 /**
1491  * An array of textual day names.
1492  * Override these values for international dates, for example...
1493  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1494  * @type Array
1495  * @static
1496  */
1497 Date.dayNames =
1498    ["Sunday",
1499     "Monday",
1500     "Tuesday",
1501     "Wednesday",
1502     "Thursday",
1503     "Friday",
1504     "Saturday"];
1505
1506 // private
1507 Date.y2kYear = 50;
1508 // private
1509 Date.monthNumbers = {
1510     Jan:0,
1511     Feb:1,
1512     Mar:2,
1513     Apr:3,
1514     May:4,
1515     Jun:5,
1516     Jul:6,
1517     Aug:7,
1518     Sep:8,
1519     Oct:9,
1520     Nov:10,
1521     Dec:11};
1522
1523 /**
1524  * Creates and returns a new Date instance with the exact same date value as the called instance.
1525  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1526  * variable will also be changed.  When the intention is to create a new variable that will not
1527  * modify the original instance, you should create a clone.
1528  *
1529  * Example of correctly cloning a date:
1530  * <pre><code>
1531 //wrong way:
1532 var orig = new Date('10/1/2006');
1533 var copy = orig;
1534 copy.setDate(5);
1535 document.write(orig);  //returns 'Thu Oct 05 2006'!
1536
1537 //correct way:
1538 var orig = new Date('10/1/2006');
1539 var copy = orig.clone();
1540 copy.setDate(5);
1541 document.write(orig);  //returns 'Thu Oct 01 2006'
1542 </code></pre>
1543  * @return {Date} The new Date instance
1544  */
1545 Date.prototype.clone = function() {
1546         return new Date(this.getTime());
1547 };
1548
1549 /**
1550  * Clears any time information from this date
1551  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1552  @return {Date} this or the clone
1553  */
1554 Date.prototype.clearTime = function(clone){
1555     if(clone){
1556         return this.clone().clearTime();
1557     }
1558     this.setHours(0);
1559     this.setMinutes(0);
1560     this.setSeconds(0);
1561     this.setMilliseconds(0);
1562     return this;
1563 };
1564
1565 // private
1566 // safari setMonth is broken
1567 if(Roo.isSafari){
1568     Date.brokenSetMonth = Date.prototype.setMonth;
1569         Date.prototype.setMonth = function(num){
1570                 if(num <= -1){
1571                         var n = Math.ceil(-num);
1572                         var back_year = Math.ceil(n/12);
1573                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1574                         this.setFullYear(this.getFullYear() - back_year);
1575                         return Date.brokenSetMonth.call(this, month);
1576                 } else {
1577                         return Date.brokenSetMonth.apply(this, arguments);
1578                 }
1579         };
1580 }
1581
1582 /** Date interval constant 
1583 * @static 
1584 * @type String */
1585 Date.MILLI = "ms";
1586 /** Date interval constant 
1587 * @static 
1588 * @type String */
1589 Date.SECOND = "s";
1590 /** Date interval constant 
1591 * @static 
1592 * @type String */
1593 Date.MINUTE = "mi";
1594 /** Date interval constant 
1595 * @static 
1596 * @type String */
1597 Date.HOUR = "h";
1598 /** Date interval constant 
1599 * @static 
1600 * @type String */
1601 Date.DAY = "d";
1602 /** Date interval constant 
1603 * @static 
1604 * @type String */
1605 Date.MONTH = "mo";
1606 /** Date interval constant 
1607 * @static 
1608 * @type String */
1609 Date.YEAR = "y";
1610
1611 /**
1612  * Provides a convenient method of performing basic date arithmetic.  This method
1613  * does not modify the Date instance being called - it creates and returns
1614  * a new Date instance containing the resulting date value.
1615  *
1616  * Examples:
1617  * <pre><code>
1618 //Basic usage:
1619 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1620 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1621
1622 //Negative values will subtract correctly:
1623 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1624 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1625
1626 //You can even chain several calls together in one line!
1627 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1628 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1629  </code></pre>
1630  *
1631  * @param {String} interval   A valid date interval enum value
1632  * @param {Number} value      The amount to add to the current date
1633  * @return {Date} The new Date instance
1634  */
1635 Date.prototype.add = function(interval, value){
1636   var d = this.clone();
1637   if (!interval || value === 0) return d;
1638   switch(interval.toLowerCase()){
1639     case Date.MILLI:
1640       d.setMilliseconds(this.getMilliseconds() + value);
1641       break;
1642     case Date.SECOND:
1643       d.setSeconds(this.getSeconds() + value);
1644       break;
1645     case Date.MINUTE:
1646       d.setMinutes(this.getMinutes() + value);
1647       break;
1648     case Date.HOUR:
1649       d.setHours(this.getHours() + value);
1650       break;
1651     case Date.DAY:
1652       d.setDate(this.getDate() + value);
1653       break;
1654     case Date.MONTH:
1655       var day = this.getDate();
1656       if(day > 28){
1657           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1658       }
1659       d.setDate(day);
1660       d.setMonth(this.getMonth() + value);
1661       break;
1662     case Date.YEAR:
1663       d.setFullYear(this.getFullYear() + value);
1664       break;
1665   }
1666   return d;
1667 };/*
1668  * Based on:
1669  * Ext JS Library 1.1.1
1670  * Copyright(c) 2006-2007, Ext JS, LLC.
1671  *
1672  * Originally Released Under LGPL - original licence link has changed is not relivant.
1673  *
1674  * Fork - LGPL
1675  * <script type="text/javascript">
1676  */
1677
1678 Roo.lib.Dom = {
1679     getViewWidth : function(full) {
1680         return full ? this.getDocumentWidth() : this.getViewportWidth();
1681     },
1682
1683     getViewHeight : function(full) {
1684         return full ? this.getDocumentHeight() : this.getViewportHeight();
1685     },
1686
1687     getDocumentHeight: function() {
1688         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1689         return Math.max(scrollHeight, this.getViewportHeight());
1690     },
1691
1692     getDocumentWidth: function() {
1693         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1694         return Math.max(scrollWidth, this.getViewportWidth());
1695     },
1696
1697     getViewportHeight: function() {
1698         var height = self.innerHeight;
1699         var mode = document.compatMode;
1700
1701         if ((mode || Roo.isIE) && !Roo.isOpera) {
1702             height = (mode == "CSS1Compat") ?
1703                      document.documentElement.clientHeight :
1704                      document.body.clientHeight;
1705         }
1706
1707         return height;
1708     },
1709
1710     getViewportWidth: function() {
1711         var width = self.innerWidth;
1712         var mode = document.compatMode;
1713
1714         if (mode || Roo.isIE) {
1715             width = (mode == "CSS1Compat") ?
1716                     document.documentElement.clientWidth :
1717                     document.body.clientWidth;
1718         }
1719         return width;
1720     },
1721
1722     isAncestor : function(p, c) {
1723         p = Roo.getDom(p);
1724         c = Roo.getDom(c);
1725         if (!p || !c) {
1726             return false;
1727         }
1728
1729         if (p.contains && !Roo.isSafari) {
1730             return p.contains(c);
1731         } else if (p.compareDocumentPosition) {
1732             return !!(p.compareDocumentPosition(c) & 16);
1733         } else {
1734             var parent = c.parentNode;
1735             while (parent) {
1736                 if (parent == p) {
1737                     return true;
1738                 }
1739                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1740                     return false;
1741                 }
1742                 parent = parent.parentNode;
1743             }
1744             return false;
1745         }
1746     },
1747
1748     getRegion : function(el) {
1749         return Roo.lib.Region.getRegion(el);
1750     },
1751
1752     getY : function(el) {
1753         return this.getXY(el)[1];
1754     },
1755
1756     getX : function(el) {
1757         return this.getXY(el)[0];
1758     },
1759
1760     getXY : function(el) {
1761         var p, pe, b, scroll, bd = document.body;
1762         el = Roo.getDom(el);
1763         var fly = Roo.lib.AnimBase.fly;
1764         if (el.getBoundingClientRect) {
1765             b = el.getBoundingClientRect();
1766             scroll = fly(document).getScroll();
1767             return [b.left + scroll.left, b.top + scroll.top];
1768         }
1769         var x = 0, y = 0;
1770
1771         p = el;
1772
1773         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1774
1775         while (p) {
1776
1777             x += p.offsetLeft;
1778             y += p.offsetTop;
1779
1780             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1781                 hasAbsolute = true;
1782             }
1783
1784             if (Roo.isGecko) {
1785                 pe = fly(p);
1786
1787                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1788                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1789
1790
1791                 x += bl;
1792                 y += bt;
1793
1794
1795                 if (p != el && pe.getStyle('overflow') != 'visible') {
1796                     x += bl;
1797                     y += bt;
1798                 }
1799             }
1800             p = p.offsetParent;
1801         }
1802
1803         if (Roo.isSafari && hasAbsolute) {
1804             x -= bd.offsetLeft;
1805             y -= bd.offsetTop;
1806         }
1807
1808         if (Roo.isGecko && !hasAbsolute) {
1809             var dbd = fly(bd);
1810             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1811             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1812         }
1813
1814         p = el.parentNode;
1815         while (p && p != bd) {
1816             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1817                 x -= p.scrollLeft;
1818                 y -= p.scrollTop;
1819             }
1820             p = p.parentNode;
1821         }
1822         return [x, y];
1823     },
1824  
1825   
1826
1827
1828     setXY : function(el, xy) {
1829         el = Roo.fly(el, '_setXY');
1830         el.position();
1831         var pts = el.translatePoints(xy);
1832         if (xy[0] !== false) {
1833             el.dom.style.left = pts.left + "px";
1834         }
1835         if (xy[1] !== false) {
1836             el.dom.style.top = pts.top + "px";
1837         }
1838     },
1839
1840     setX : function(el, x) {
1841         this.setXY(el, [x, false]);
1842     },
1843
1844     setY : function(el, y) {
1845         this.setXY(el, [false, y]);
1846     }
1847 };
1848 /*
1849  * Portions of this file are based on pieces of Yahoo User Interface Library
1850  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1851  * YUI licensed under the BSD License:
1852  * http://developer.yahoo.net/yui/license.txt
1853  * <script type="text/javascript">
1854  *
1855  */
1856
1857 Roo.lib.Event = function() {
1858     var loadComplete = false;
1859     var listeners = [];
1860     var unloadListeners = [];
1861     var retryCount = 0;
1862     var onAvailStack = [];
1863     var counter = 0;
1864     var lastError = null;
1865
1866     return {
1867         POLL_RETRYS: 200,
1868         POLL_INTERVAL: 20,
1869         EL: 0,
1870         TYPE: 1,
1871         FN: 2,
1872         WFN: 3,
1873         OBJ: 3,
1874         ADJ_SCOPE: 4,
1875         _interval: null,
1876
1877         startInterval: function() {
1878             if (!this._interval) {
1879                 var self = this;
1880                 var callback = function() {
1881                     self._tryPreloadAttach();
1882                 };
1883                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1884
1885             }
1886         },
1887
1888         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1889             onAvailStack.push({ id:         p_id,
1890                 fn:         p_fn,
1891                 obj:        p_obj,
1892                 override:   p_override,
1893                 checkReady: false    });
1894
1895             retryCount = this.POLL_RETRYS;
1896             this.startInterval();
1897         },
1898
1899
1900         addListener: function(el, eventName, fn) {
1901             el = Roo.getDom(el);
1902             if (!el || !fn) {
1903                 return false;
1904             }
1905
1906             if ("unload" == eventName) {
1907                 unloadListeners[unloadListeners.length] =
1908                 [el, eventName, fn];
1909                 return true;
1910             }
1911
1912             var wrappedFn = function(e) {
1913                 return fn(Roo.lib.Event.getEvent(e));
1914             };
1915
1916             var li = [el, eventName, fn, wrappedFn];
1917
1918             var index = listeners.length;
1919             listeners[index] = li;
1920
1921             this.doAdd(el, eventName, wrappedFn, false);
1922             return true;
1923
1924         },
1925
1926
1927         removeListener: function(el, eventName, fn) {
1928             var i, len;
1929
1930             el = Roo.getDom(el);
1931
1932             if(!fn) {
1933                 return this.purgeElement(el, false, eventName);
1934             }
1935
1936
1937             if ("unload" == eventName) {
1938
1939                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1940                     var li = unloadListeners[i];
1941                     if (li &&
1942                         li[0] == el &&
1943                         li[1] == eventName &&
1944                         li[2] == fn) {
1945                         unloadListeners.splice(i, 1);
1946                         return true;
1947                     }
1948                 }
1949
1950                 return false;
1951             }
1952
1953             var cacheItem = null;
1954
1955
1956             var index = arguments[3];
1957
1958             if ("undefined" == typeof index) {
1959                 index = this._getCacheIndex(el, eventName, fn);
1960             }
1961
1962             if (index >= 0) {
1963                 cacheItem = listeners[index];
1964             }
1965
1966             if (!el || !cacheItem) {
1967                 return false;
1968             }
1969
1970             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1971
1972             delete listeners[index][this.WFN];
1973             delete listeners[index][this.FN];
1974             listeners.splice(index, 1);
1975
1976             return true;
1977
1978         },
1979
1980
1981         getTarget: function(ev, resolveTextNode) {
1982             ev = ev.browserEvent || ev;
1983             var t = ev.target || ev.srcElement;
1984             return this.resolveTextNode(t);
1985         },
1986
1987
1988         resolveTextNode: function(node) {
1989             if (Roo.isSafari && node && 3 == node.nodeType) {
1990                 return node.parentNode;
1991             } else {
1992                 return node;
1993             }
1994         },
1995
1996
1997         getPageX: function(ev) {
1998             ev = ev.browserEvent || ev;
1999             var x = ev.pageX;
2000             if (!x && 0 !== x) {
2001                 x = ev.clientX || 0;
2002
2003                 if (Roo.isIE) {
2004                     x += this.getScroll()[1];
2005                 }
2006             }
2007
2008             return x;
2009         },
2010
2011
2012         getPageY: function(ev) {
2013             ev = ev.browserEvent || ev;
2014             var y = ev.pageY;
2015             if (!y && 0 !== y) {
2016                 y = ev.clientY || 0;
2017
2018                 if (Roo.isIE) {
2019                     y += this.getScroll()[0];
2020                 }
2021             }
2022
2023
2024             return y;
2025         },
2026
2027
2028         getXY: function(ev) {
2029             ev = ev.browserEvent || ev;
2030             return [this.getPageX(ev), this.getPageY(ev)];
2031         },
2032
2033
2034         getRelatedTarget: function(ev) {
2035             ev = ev.browserEvent || ev;
2036             var t = ev.relatedTarget;
2037             if (!t) {
2038                 if (ev.type == "mouseout") {
2039                     t = ev.toElement;
2040                 } else if (ev.type == "mouseover") {
2041                     t = ev.fromElement;
2042                 }
2043             }
2044
2045             return this.resolveTextNode(t);
2046         },
2047
2048
2049         getTime: function(ev) {
2050             ev = ev.browserEvent || ev;
2051             if (!ev.time) {
2052                 var t = new Date().getTime();
2053                 try {
2054                     ev.time = t;
2055                 } catch(ex) {
2056                     this.lastError = ex;
2057                     return t;
2058                 }
2059             }
2060
2061             return ev.time;
2062         },
2063
2064
2065         stopEvent: function(ev) {
2066             this.stopPropagation(ev);
2067             this.preventDefault(ev);
2068         },
2069
2070
2071         stopPropagation: function(ev) {
2072             ev = ev.browserEvent || ev;
2073             if (ev.stopPropagation) {
2074                 ev.stopPropagation();
2075             } else {
2076                 ev.cancelBubble = true;
2077             }
2078         },
2079
2080
2081         preventDefault: function(ev) {
2082             ev = ev.browserEvent || ev;
2083             if(ev.preventDefault) {
2084                 ev.preventDefault();
2085             } else {
2086                 ev.returnValue = false;
2087             }
2088         },
2089
2090
2091         getEvent: function(e) {
2092             var ev = e || window.event;
2093             if (!ev) {
2094                 var c = this.getEvent.caller;
2095                 while (c) {
2096                     ev = c.arguments[0];
2097                     if (ev && Event == ev.constructor) {
2098                         break;
2099                     }
2100                     c = c.caller;
2101                 }
2102             }
2103             return ev;
2104         },
2105
2106
2107         getCharCode: function(ev) {
2108             ev = ev.browserEvent || ev;
2109             return ev.charCode || ev.keyCode || 0;
2110         },
2111
2112
2113         _getCacheIndex: function(el, eventName, fn) {
2114             for (var i = 0,len = listeners.length; i < len; ++i) {
2115                 var li = listeners[i];
2116                 if (li &&
2117                     li[this.FN] == fn &&
2118                     li[this.EL] == el &&
2119                     li[this.TYPE] == eventName) {
2120                     return i;
2121                 }
2122             }
2123
2124             return -1;
2125         },
2126
2127
2128         elCache: {},
2129
2130
2131         getEl: function(id) {
2132             return document.getElementById(id);
2133         },
2134
2135
2136         clearCache: function() {
2137         },
2138
2139
2140         _load: function(e) {
2141             loadComplete = true;
2142             var EU = Roo.lib.Event;
2143
2144
2145             if (Roo.isIE) {
2146                 EU.doRemove(window, "load", EU._load);
2147             }
2148         },
2149
2150
2151         _tryPreloadAttach: function() {
2152
2153             if (this.locked) {
2154                 return false;
2155             }
2156
2157             this.locked = true;
2158
2159
2160             var tryAgain = !loadComplete;
2161             if (!tryAgain) {
2162                 tryAgain = (retryCount > 0);
2163             }
2164
2165
2166             var notAvail = [];
2167             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2168                 var item = onAvailStack[i];
2169                 if (item) {
2170                     var el = this.getEl(item.id);
2171
2172                     if (el) {
2173                         if (!item.checkReady ||
2174                             loadComplete ||
2175                             el.nextSibling ||
2176                             (document && document.body)) {
2177
2178                             var scope = el;
2179                             if (item.override) {
2180                                 if (item.override === true) {
2181                                     scope = item.obj;
2182                                 } else {
2183                                     scope = item.override;
2184                                 }
2185                             }
2186                             item.fn.call(scope, item.obj);
2187                             onAvailStack[i] = null;
2188                         }
2189                     } else {
2190                         notAvail.push(item);
2191                     }
2192                 }
2193             }
2194
2195             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2196
2197             if (tryAgain) {
2198
2199                 this.startInterval();
2200             } else {
2201                 clearInterval(this._interval);
2202                 this._interval = null;
2203             }
2204
2205             this.locked = false;
2206
2207             return true;
2208
2209         },
2210
2211
2212         purgeElement: function(el, recurse, eventName) {
2213             var elListeners = this.getListeners(el, eventName);
2214             if (elListeners) {
2215                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2216                     var l = elListeners[i];
2217                     this.removeListener(el, l.type, l.fn);
2218                 }
2219             }
2220
2221             if (recurse && el && el.childNodes) {
2222                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2223                     this.purgeElement(el.childNodes[i], recurse, eventName);
2224                 }
2225             }
2226         },
2227
2228
2229         getListeners: function(el, eventName) {
2230             var results = [], searchLists;
2231             if (!eventName) {
2232                 searchLists = [listeners, unloadListeners];
2233             } else if (eventName == "unload") {
2234                 searchLists = [unloadListeners];
2235             } else {
2236                 searchLists = [listeners];
2237             }
2238
2239             for (var j = 0; j < searchLists.length; ++j) {
2240                 var searchList = searchLists[j];
2241                 if (searchList && searchList.length > 0) {
2242                     for (var i = 0,len = searchList.length; i < len; ++i) {
2243                         var l = searchList[i];
2244                         if (l && l[this.EL] === el &&
2245                             (!eventName || eventName === l[this.TYPE])) {
2246                             results.push({
2247                                 type:   l[this.TYPE],
2248                                 fn:     l[this.FN],
2249                                 obj:    l[this.OBJ],
2250                                 adjust: l[this.ADJ_SCOPE],
2251                                 index:  i
2252                             });
2253                         }
2254                     }
2255                 }
2256             }
2257
2258             return (results.length) ? results : null;
2259         },
2260
2261
2262         _unload: function(e) {
2263
2264             var EU = Roo.lib.Event, i, j, l, len, index;
2265
2266             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2267                 l = unloadListeners[i];
2268                 if (l) {
2269                     var scope = window;
2270                     if (l[EU.ADJ_SCOPE]) {
2271                         if (l[EU.ADJ_SCOPE] === true) {
2272                             scope = l[EU.OBJ];
2273                         } else {
2274                             scope = l[EU.ADJ_SCOPE];
2275                         }
2276                     }
2277                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2278                     unloadListeners[i] = null;
2279                     l = null;
2280                     scope = null;
2281                 }
2282             }
2283
2284             unloadListeners = null;
2285
2286             if (listeners && listeners.length > 0) {
2287                 j = listeners.length;
2288                 while (j) {
2289                     index = j - 1;
2290                     l = listeners[index];
2291                     if (l) {
2292                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2293                                 l[EU.FN], index);
2294                     }
2295                     j = j - 1;
2296                 }
2297                 l = null;
2298
2299                 EU.clearCache();
2300             }
2301
2302             EU.doRemove(window, "unload", EU._unload);
2303
2304         },
2305
2306
2307         getScroll: function() {
2308             var dd = document.documentElement, db = document.body;
2309             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2310                 return [dd.scrollTop, dd.scrollLeft];
2311             } else if (db) {
2312                 return [db.scrollTop, db.scrollLeft];
2313             } else {
2314                 return [0, 0];
2315             }
2316         },
2317
2318
2319         doAdd: function () {
2320             if (window.addEventListener) {
2321                 return function(el, eventName, fn, capture) {
2322                     el.addEventListener(eventName, fn, (capture));
2323                 };
2324             } else if (window.attachEvent) {
2325                 return function(el, eventName, fn, capture) {
2326                     el.attachEvent("on" + eventName, fn);
2327                 };
2328             } else {
2329                 return function() {
2330                 };
2331             }
2332         }(),
2333
2334
2335         doRemove: function() {
2336             if (window.removeEventListener) {
2337                 return function (el, eventName, fn, capture) {
2338                     el.removeEventListener(eventName, fn, (capture));
2339                 };
2340             } else if (window.detachEvent) {
2341                 return function (el, eventName, fn) {
2342                     el.detachEvent("on" + eventName, fn);
2343                 };
2344             } else {
2345                 return function() {
2346                 };
2347             }
2348         }()
2349     };
2350     
2351 }();
2352 (function() {     
2353    
2354     var E = Roo.lib.Event;
2355     E.on = E.addListener;
2356     E.un = E.removeListener;
2357
2358     if (document && document.body) {
2359         E._load();
2360     } else {
2361         E.doAdd(window, "load", E._load);
2362     }
2363     E.doAdd(window, "unload", E._unload);
2364     E._tryPreloadAttach();
2365 })();
2366
2367 /*
2368  * Portions of this file are based on pieces of Yahoo User Interface Library
2369  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2370  * YUI licensed under the BSD License:
2371  * http://developer.yahoo.net/yui/license.txt
2372  * <script type="text/javascript">
2373  *
2374  */
2375
2376 (function() {
2377     
2378     Roo.lib.Ajax = {
2379         request : function(method, uri, cb, data, options) {
2380             if(options){
2381                 var hs = options.headers;
2382                 if(hs){
2383                     for(var h in hs){
2384                         if(hs.hasOwnProperty(h)){
2385                             this.initHeader(h, hs[h], false);
2386                         }
2387                     }
2388                 }
2389                 if(options.xmlData){
2390                     this.initHeader('Content-Type', 'text/xml', false);
2391                     method = 'POST';
2392                     data = options.xmlData;
2393                 }
2394             }
2395
2396             return this.asyncRequest(method, uri, cb, data);
2397         },
2398
2399         serializeForm : function(form) {
2400             if(typeof form == 'string') {
2401                 form = (document.getElementById(form) || document.forms[form]);
2402             }
2403
2404             var el, name, val, disabled, data = '', hasSubmit = false;
2405             for (var i = 0; i < form.elements.length; i++) {
2406                 el = form.elements[i];
2407                 disabled = form.elements[i].disabled;
2408                 name = form.elements[i].name;
2409                 val = form.elements[i].value;
2410
2411                 if (!disabled && name){
2412                     switch (el.type)
2413                             {
2414                         case 'select-one':
2415                         case 'select-multiple':
2416                             for (var j = 0; j < el.options.length; j++) {
2417                                 if (el.options[j].selected) {
2418                                     if (Roo.isIE) {
2419                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2420                                     }
2421                                     else {
2422                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2423                                     }
2424                                 }
2425                             }
2426                             break;
2427                         case 'radio':
2428                         case 'checkbox':
2429                             if (el.checked) {
2430                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2431                             }
2432                             break;
2433                         case 'file':
2434
2435                         case undefined:
2436
2437                         case 'reset':
2438
2439                         case 'button':
2440
2441                             break;
2442                         case 'submit':
2443                             if(hasSubmit == false) {
2444                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2445                                 hasSubmit = true;
2446                             }
2447                             break;
2448                         default:
2449                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2450                             break;
2451                     }
2452                 }
2453             }
2454             data = data.substr(0, data.length - 1);
2455             return data;
2456         },
2457
2458         headers:{},
2459
2460         hasHeaders:false,
2461
2462         useDefaultHeader:true,
2463
2464         defaultPostHeader:'application/x-www-form-urlencoded',
2465
2466         useDefaultXhrHeader:true,
2467
2468         defaultXhrHeader:'XMLHttpRequest',
2469
2470         hasDefaultHeaders:true,
2471
2472         defaultHeaders:{},
2473
2474         poll:{},
2475
2476         timeout:{},
2477
2478         pollInterval:50,
2479
2480         transactionId:0,
2481
2482         setProgId:function(id)
2483         {
2484             this.activeX.unshift(id);
2485         },
2486
2487         setDefaultPostHeader:function(b)
2488         {
2489             this.useDefaultHeader = b;
2490         },
2491
2492         setDefaultXhrHeader:function(b)
2493         {
2494             this.useDefaultXhrHeader = b;
2495         },
2496
2497         setPollingInterval:function(i)
2498         {
2499             if (typeof i == 'number' && isFinite(i)) {
2500                 this.pollInterval = i;
2501             }
2502         },
2503
2504         createXhrObject:function(transactionId)
2505         {
2506             var obj,http;
2507             try
2508             {
2509
2510                 http = new XMLHttpRequest();
2511
2512                 obj = { conn:http, tId:transactionId };
2513             }
2514             catch(e)
2515             {
2516                 for (var i = 0; i < this.activeX.length; ++i) {
2517                     try
2518                     {
2519
2520                         http = new ActiveXObject(this.activeX[i]);
2521
2522                         obj = { conn:http, tId:transactionId };
2523                         break;
2524                     }
2525                     catch(e) {
2526                     }
2527                 }
2528             }
2529             finally
2530             {
2531                 return obj;
2532             }
2533         },
2534
2535         getConnectionObject:function()
2536         {
2537             var o;
2538             var tId = this.transactionId;
2539
2540             try
2541             {
2542                 o = this.createXhrObject(tId);
2543                 if (o) {
2544                     this.transactionId++;
2545                 }
2546             }
2547             catch(e) {
2548             }
2549             finally
2550             {
2551                 return o;
2552             }
2553         },
2554
2555         asyncRequest:function(method, uri, callback, postData)
2556         {
2557             var o = this.getConnectionObject();
2558
2559             if (!o) {
2560                 return null;
2561             }
2562             else {
2563                 o.conn.open(method, uri, true);
2564
2565                 if (this.useDefaultXhrHeader) {
2566                     if (!this.defaultHeaders['X-Requested-With']) {
2567                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2568                     }
2569                 }
2570
2571                 if(postData && this.useDefaultHeader){
2572                     this.initHeader('Content-Type', this.defaultPostHeader);
2573                 }
2574
2575                  if (this.hasDefaultHeaders || this.hasHeaders) {
2576                     this.setHeader(o);
2577                 }
2578
2579                 this.handleReadyState(o, callback);
2580                 o.conn.send(postData || null);
2581
2582                 return o;
2583             }
2584         },
2585
2586         handleReadyState:function(o, callback)
2587         {
2588             var oConn = this;
2589
2590             if (callback && callback.timeout) {
2591                 this.timeout[o.tId] = window.setTimeout(function() {
2592                     oConn.abort(o, callback, true);
2593                 }, callback.timeout);
2594             }
2595
2596             this.poll[o.tId] = window.setInterval(
2597                     function() {
2598                         if (o.conn && o.conn.readyState == 4) {
2599                             window.clearInterval(oConn.poll[o.tId]);
2600                             delete oConn.poll[o.tId];
2601
2602                             if(callback && callback.timeout) {
2603                                 window.clearTimeout(oConn.timeout[o.tId]);
2604                                 delete oConn.timeout[o.tId];
2605                             }
2606
2607                             oConn.handleTransactionResponse(o, callback);
2608                         }
2609                     }
2610                     , this.pollInterval);
2611         },
2612
2613         handleTransactionResponse:function(o, callback, isAbort)
2614         {
2615
2616             if (!callback) {
2617                 this.releaseObject(o);
2618                 return;
2619             }
2620
2621             var httpStatus, responseObject;
2622
2623             try
2624             {
2625                 if (o.conn.status !== undefined && o.conn.status != 0) {
2626                     httpStatus = o.conn.status;
2627                 }
2628                 else {
2629                     httpStatus = 13030;
2630                 }
2631             }
2632             catch(e) {
2633
2634
2635                 httpStatus = 13030;
2636             }
2637
2638             if (httpStatus >= 200 && httpStatus < 300) {
2639                 responseObject = this.createResponseObject(o, callback.argument);
2640                 if (callback.success) {
2641                     if (!callback.scope) {
2642                         callback.success(responseObject);
2643                     }
2644                     else {
2645
2646
2647                         callback.success.apply(callback.scope, [responseObject]);
2648                     }
2649                 }
2650             }
2651             else {
2652                 switch (httpStatus) {
2653
2654                     case 12002:
2655                     case 12029:
2656                     case 12030:
2657                     case 12031:
2658                     case 12152:
2659                     case 13030:
2660                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2661                         if (callback.failure) {
2662                             if (!callback.scope) {
2663                                 callback.failure(responseObject);
2664                             }
2665                             else {
2666                                 callback.failure.apply(callback.scope, [responseObject]);
2667                             }
2668                         }
2669                         break;
2670                     default:
2671                         responseObject = this.createResponseObject(o, callback.argument);
2672                         if (callback.failure) {
2673                             if (!callback.scope) {
2674                                 callback.failure(responseObject);
2675                             }
2676                             else {
2677                                 callback.failure.apply(callback.scope, [responseObject]);
2678                             }
2679                         }
2680                 }
2681             }
2682
2683             this.releaseObject(o);
2684             responseObject = null;
2685         },
2686
2687         createResponseObject:function(o, callbackArg)
2688         {
2689             var obj = {};
2690             var headerObj = {};
2691
2692             try
2693             {
2694                 var headerStr = o.conn.getAllResponseHeaders();
2695                 var header = headerStr.split('\n');
2696                 for (var i = 0; i < header.length; i++) {
2697                     var delimitPos = header[i].indexOf(':');
2698                     if (delimitPos != -1) {
2699                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2700                     }
2701                 }
2702             }
2703             catch(e) {
2704             }
2705
2706             obj.tId = o.tId;
2707             obj.status = o.conn.status;
2708             obj.statusText = o.conn.statusText;
2709             obj.getResponseHeader = headerObj;
2710             obj.getAllResponseHeaders = headerStr;
2711             obj.responseText = o.conn.responseText;
2712             obj.responseXML = o.conn.responseXML;
2713
2714             if (typeof callbackArg !== undefined) {
2715                 obj.argument = callbackArg;
2716             }
2717
2718             return obj;
2719         },
2720
2721         createExceptionObject:function(tId, callbackArg, isAbort)
2722         {
2723             var COMM_CODE = 0;
2724             var COMM_ERROR = 'communication failure';
2725             var ABORT_CODE = -1;
2726             var ABORT_ERROR = 'transaction aborted';
2727
2728             var obj = {};
2729
2730             obj.tId = tId;
2731             if (isAbort) {
2732                 obj.status = ABORT_CODE;
2733                 obj.statusText = ABORT_ERROR;
2734             }
2735             else {
2736                 obj.status = COMM_CODE;
2737                 obj.statusText = COMM_ERROR;
2738             }
2739
2740             if (callbackArg) {
2741                 obj.argument = callbackArg;
2742             }
2743
2744             return obj;
2745         },
2746
2747         initHeader:function(label, value, isDefault)
2748         {
2749             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2750
2751             if (headerObj[label] === undefined) {
2752                 headerObj[label] = value;
2753             }
2754             else {
2755
2756
2757                 headerObj[label] = value + "," + headerObj[label];
2758             }
2759
2760             if (isDefault) {
2761                 this.hasDefaultHeaders = true;
2762             }
2763             else {
2764                 this.hasHeaders = true;
2765             }
2766         },
2767
2768
2769         setHeader:function(o)
2770         {
2771             if (this.hasDefaultHeaders) {
2772                 for (var prop in this.defaultHeaders) {
2773                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2774                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2775                     }
2776                 }
2777             }
2778
2779             if (this.hasHeaders) {
2780                 for (var prop in this.headers) {
2781                     if (this.headers.hasOwnProperty(prop)) {
2782                         o.conn.setRequestHeader(prop, this.headers[prop]);
2783                     }
2784                 }
2785                 this.headers = {};
2786                 this.hasHeaders = false;
2787             }
2788         },
2789
2790         resetDefaultHeaders:function() {
2791             delete this.defaultHeaders;
2792             this.defaultHeaders = {};
2793             this.hasDefaultHeaders = false;
2794         },
2795
2796         abort:function(o, callback, isTimeout)
2797         {
2798             if(this.isCallInProgress(o)) {
2799                 o.conn.abort();
2800                 window.clearInterval(this.poll[o.tId]);
2801                 delete this.poll[o.tId];
2802                 if (isTimeout) {
2803                     delete this.timeout[o.tId];
2804                 }
2805
2806                 this.handleTransactionResponse(o, callback, true);
2807
2808                 return true;
2809             }
2810             else {
2811                 return false;
2812             }
2813         },
2814
2815
2816         isCallInProgress:function(o)
2817         {
2818             if (o && o.conn) {
2819                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2820             }
2821             else {
2822
2823                 return false;
2824             }
2825         },
2826
2827
2828         releaseObject:function(o)
2829         {
2830
2831             o.conn = null;
2832
2833             o = null;
2834         },
2835
2836         activeX:[
2837         'MSXML2.XMLHTTP.3.0',
2838         'MSXML2.XMLHTTP',
2839         'Microsoft.XMLHTTP'
2840         ]
2841
2842
2843     };
2844 })();/*
2845  * Portions of this file are based on pieces of Yahoo User Interface Library
2846  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2847  * YUI licensed under the BSD License:
2848  * http://developer.yahoo.net/yui/license.txt
2849  * <script type="text/javascript">
2850  *
2851  */
2852
2853 Roo.lib.Region = function(t, r, b, l) {
2854     this.top = t;
2855     this[1] = t;
2856     this.right = r;
2857     this.bottom = b;
2858     this.left = l;
2859     this[0] = l;
2860 };
2861
2862
2863 Roo.lib.Region.prototype = {
2864     contains : function(region) {
2865         return ( region.left >= this.left &&
2866                  region.right <= this.right &&
2867                  region.top >= this.top &&
2868                  region.bottom <= this.bottom    );
2869
2870     },
2871
2872     getArea : function() {
2873         return ( (this.bottom - this.top) * (this.right - this.left) );
2874     },
2875
2876     intersect : function(region) {
2877         var t = Math.max(this.top, region.top);
2878         var r = Math.min(this.right, region.right);
2879         var b = Math.min(this.bottom, region.bottom);
2880         var l = Math.max(this.left, region.left);
2881
2882         if (b >= t && r >= l) {
2883             return new Roo.lib.Region(t, r, b, l);
2884         } else {
2885             return null;
2886         }
2887     },
2888     union : function(region) {
2889         var t = Math.min(this.top, region.top);
2890         var r = Math.max(this.right, region.right);
2891         var b = Math.max(this.bottom, region.bottom);
2892         var l = Math.min(this.left, region.left);
2893
2894         return new Roo.lib.Region(t, r, b, l);
2895     },
2896
2897     adjust : function(t, l, b, r) {
2898         this.top += t;
2899         this.left += l;
2900         this.right += r;
2901         this.bottom += b;
2902         return this;
2903     }
2904 };
2905
2906 Roo.lib.Region.getRegion = function(el) {
2907     var p = Roo.lib.Dom.getXY(el);
2908
2909     var t = p[1];
2910     var r = p[0] + el.offsetWidth;
2911     var b = p[1] + el.offsetHeight;
2912     var l = p[0];
2913
2914     return new Roo.lib.Region(t, r, b, l);
2915 };
2916 /*
2917  * Portions of this file are based on pieces of Yahoo User Interface Library
2918  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2919  * YUI licensed under the BSD License:
2920  * http://developer.yahoo.net/yui/license.txt
2921  * <script type="text/javascript">
2922  *
2923  */
2924 //@@dep Roo.lib.Region
2925
2926
2927 Roo.lib.Point = function(x, y) {
2928     if (x instanceof Array) {
2929         y = x[1];
2930         x = x[0];
2931     }
2932     this.x = this.right = this.left = this[0] = x;
2933     this.y = this.top = this.bottom = this[1] = y;
2934 };
2935
2936 Roo.lib.Point.prototype = new Roo.lib.Region();
2937 /*
2938  * Portions of this file are based on pieces of Yahoo User Interface Library
2939  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2940  * YUI licensed under the BSD License:
2941  * http://developer.yahoo.net/yui/license.txt
2942  * <script type="text/javascript">
2943  *
2944  */
2945  
2946 (function() {   
2947
2948     Roo.lib.Anim = {
2949         scroll : function(el, args, duration, easing, cb, scope) {
2950             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2951         },
2952
2953         motion : function(el, args, duration, easing, cb, scope) {
2954             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2955         },
2956
2957         color : function(el, args, duration, easing, cb, scope) {
2958             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2959         },
2960
2961         run : function(el, args, duration, easing, cb, scope, type) {
2962             type = type || Roo.lib.AnimBase;
2963             if (typeof easing == "string") {
2964                 easing = Roo.lib.Easing[easing];
2965             }
2966             var anim = new type(el, args, duration, easing);
2967             anim.animateX(function() {
2968                 Roo.callback(cb, scope);
2969             });
2970             return anim;
2971         }
2972     };
2973 })();/*
2974  * Portions of this file are based on pieces of Yahoo User Interface Library
2975  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2976  * YUI licensed under the BSD License:
2977  * http://developer.yahoo.net/yui/license.txt
2978  * <script type="text/javascript">
2979  *
2980  */
2981
2982 (function() {    
2983     var libFlyweight;
2984     
2985     function fly(el) {
2986         if (!libFlyweight) {
2987             libFlyweight = new Roo.Element.Flyweight();
2988         }
2989         libFlyweight.dom = el;
2990         return libFlyweight;
2991     }
2992
2993     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2994     
2995    
2996     
2997     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2998         if (el) {
2999             this.init(el, attributes, duration, method);
3000         }
3001     };
3002
3003     Roo.lib.AnimBase.fly = fly;
3004     
3005     
3006     
3007     Roo.lib.AnimBase.prototype = {
3008
3009         toString: function() {
3010             var el = this.getEl();
3011             var id = el.id || el.tagName;
3012             return ("Anim " + id);
3013         },
3014
3015         patterns: {
3016             noNegatives:        /width|height|opacity|padding/i,
3017             offsetAttribute:  /^((width|height)|(top|left))$/,
3018             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3019             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3020         },
3021
3022
3023         doMethod: function(attr, start, end) {
3024             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3025         },
3026
3027
3028         setAttribute: function(attr, val, unit) {
3029             if (this.patterns.noNegatives.test(attr)) {
3030                 val = (val > 0) ? val : 0;
3031             }
3032
3033             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3034         },
3035
3036
3037         getAttribute: function(attr) {
3038             var el = this.getEl();
3039             var val = fly(el).getStyle(attr);
3040
3041             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3042                 return parseFloat(val);
3043             }
3044
3045             var a = this.patterns.offsetAttribute.exec(attr) || [];
3046             var pos = !!( a[3] );
3047             var box = !!( a[2] );
3048
3049
3050             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3051                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3052             } else {
3053                 val = 0;
3054             }
3055
3056             return val;
3057         },
3058
3059
3060         getDefaultUnit: function(attr) {
3061             if (this.patterns.defaultUnit.test(attr)) {
3062                 return 'px';
3063             }
3064
3065             return '';
3066         },
3067
3068         animateX : function(callback, scope) {
3069             var f = function() {
3070                 this.onComplete.removeListener(f);
3071                 if (typeof callback == "function") {
3072                     callback.call(scope || this, this);
3073                 }
3074             };
3075             this.onComplete.addListener(f, this);
3076             this.animate();
3077         },
3078
3079
3080         setRuntimeAttribute: function(attr) {
3081             var start;
3082             var end;
3083             var attributes = this.attributes;
3084
3085             this.runtimeAttributes[attr] = {};
3086
3087             var isset = function(prop) {
3088                 return (typeof prop !== 'undefined');
3089             };
3090
3091             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3092                 return false;
3093             }
3094
3095             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3096
3097
3098             if (isset(attributes[attr]['to'])) {
3099                 end = attributes[attr]['to'];
3100             } else if (isset(attributes[attr]['by'])) {
3101                 if (start.constructor == Array) {
3102                     end = [];
3103                     for (var i = 0, len = start.length; i < len; ++i) {
3104                         end[i] = start[i] + attributes[attr]['by'][i];
3105                     }
3106                 } else {
3107                     end = start + attributes[attr]['by'];
3108                 }
3109             }
3110
3111             this.runtimeAttributes[attr].start = start;
3112             this.runtimeAttributes[attr].end = end;
3113
3114
3115             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3116         },
3117
3118
3119         init: function(el, attributes, duration, method) {
3120
3121             var isAnimated = false;
3122
3123
3124             var startTime = null;
3125
3126
3127             var actualFrames = 0;
3128
3129
3130             el = Roo.getDom(el);
3131
3132
3133             this.attributes = attributes || {};
3134
3135
3136             this.duration = duration || 1;
3137
3138
3139             this.method = method || Roo.lib.Easing.easeNone;
3140
3141
3142             this.useSeconds = true;
3143
3144
3145             this.currentFrame = 0;
3146
3147
3148             this.totalFrames = Roo.lib.AnimMgr.fps;
3149
3150
3151             this.getEl = function() {
3152                 return el;
3153             };
3154
3155
3156             this.isAnimated = function() {
3157                 return isAnimated;
3158             };
3159
3160
3161             this.getStartTime = function() {
3162                 return startTime;
3163             };
3164
3165             this.runtimeAttributes = {};
3166
3167
3168             this.animate = function() {
3169                 if (this.isAnimated()) {
3170                     return false;
3171                 }
3172
3173                 this.currentFrame = 0;
3174
3175                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3176
3177                 Roo.lib.AnimMgr.registerElement(this);
3178             };
3179
3180
3181             this.stop = function(finish) {
3182                 if (finish) {
3183                     this.currentFrame = this.totalFrames;
3184                     this._onTween.fire();
3185                 }
3186                 Roo.lib.AnimMgr.stop(this);
3187             };
3188
3189             var onStart = function() {
3190                 this.onStart.fire();
3191
3192                 this.runtimeAttributes = {};
3193                 for (var attr in this.attributes) {
3194                     this.setRuntimeAttribute(attr);
3195                 }
3196
3197                 isAnimated = true;
3198                 actualFrames = 0;
3199                 startTime = new Date();
3200             };
3201
3202
3203             var onTween = function() {
3204                 var data = {
3205                     duration: new Date() - this.getStartTime(),
3206                     currentFrame: this.currentFrame
3207                 };
3208
3209                 data.toString = function() {
3210                     return (
3211                             'duration: ' + data.duration +
3212                             ', currentFrame: ' + data.currentFrame
3213                             );
3214                 };
3215
3216                 this.onTween.fire(data);
3217
3218                 var runtimeAttributes = this.runtimeAttributes;
3219
3220                 for (var attr in runtimeAttributes) {
3221                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3222                 }
3223
3224                 actualFrames += 1;
3225             };
3226
3227             var onComplete = function() {
3228                 var actual_duration = (new Date() - startTime) / 1000 ;
3229
3230                 var data = {
3231                     duration: actual_duration,
3232                     frames: actualFrames,
3233                     fps: actualFrames / actual_duration
3234                 };
3235
3236                 data.toString = function() {
3237                     return (
3238                             'duration: ' + data.duration +
3239                             ', frames: ' + data.frames +
3240                             ', fps: ' + data.fps
3241                             );
3242                 };
3243
3244                 isAnimated = false;
3245                 actualFrames = 0;
3246                 this.onComplete.fire(data);
3247             };
3248
3249
3250             this._onStart = new Roo.util.Event(this);
3251             this.onStart = new Roo.util.Event(this);
3252             this.onTween = new Roo.util.Event(this);
3253             this._onTween = new Roo.util.Event(this);
3254             this.onComplete = new Roo.util.Event(this);
3255             this._onComplete = new Roo.util.Event(this);
3256             this._onStart.addListener(onStart);
3257             this._onTween.addListener(onTween);
3258             this._onComplete.addListener(onComplete);
3259         }
3260     };
3261 })();
3262 /*
3263  * Portions of this file are based on pieces of Yahoo User Interface Library
3264  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3265  * YUI licensed under the BSD License:
3266  * http://developer.yahoo.net/yui/license.txt
3267  * <script type="text/javascript">
3268  *
3269  */
3270
3271 Roo.lib.AnimMgr = new function() {
3272
3273         var thread = null;
3274
3275
3276         var queue = [];
3277
3278
3279         var tweenCount = 0;
3280
3281
3282         this.fps = 1000;
3283
3284
3285         this.delay = 1;
3286
3287
3288         this.registerElement = function(tween) {
3289             queue[queue.length] = tween;
3290             tweenCount += 1;
3291             tween._onStart.fire();
3292             this.start();
3293         };
3294
3295
3296         this.unRegister = function(tween, index) {
3297             tween._onComplete.fire();
3298             index = index || getIndex(tween);
3299             if (index != -1) {
3300                 queue.splice(index, 1);
3301             }
3302
3303             tweenCount -= 1;
3304             if (tweenCount <= 0) {
3305                 this.stop();
3306             }
3307         };
3308
3309
3310         this.start = function() {
3311             if (thread === null) {
3312                 thread = setInterval(this.run, this.delay);
3313             }
3314         };
3315
3316
3317         this.stop = function(tween) {
3318             if (!tween) {
3319                 clearInterval(thread);
3320
3321                 for (var i = 0, len = queue.length; i < len; ++i) {
3322                     if (queue[0].isAnimated()) {
3323                         this.unRegister(queue[0], 0);
3324                     }
3325                 }
3326
3327                 queue = [];
3328                 thread = null;
3329                 tweenCount = 0;
3330             }
3331             else {
3332                 this.unRegister(tween);
3333             }
3334         };
3335
3336
3337         this.run = function() {
3338             for (var i = 0, len = queue.length; i < len; ++i) {
3339                 var tween = queue[i];
3340                 if (!tween || !tween.isAnimated()) {
3341                     continue;
3342                 }
3343
3344                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3345                 {
3346                     tween.currentFrame += 1;
3347
3348                     if (tween.useSeconds) {
3349                         correctFrame(tween);
3350                     }
3351                     tween._onTween.fire();
3352                 }
3353                 else {
3354                     Roo.lib.AnimMgr.stop(tween, i);
3355                 }
3356             }
3357         };
3358
3359         var getIndex = function(anim) {
3360             for (var i = 0, len = queue.length; i < len; ++i) {
3361                 if (queue[i] == anim) {
3362                     return i;
3363                 }
3364             }
3365             return -1;
3366         };
3367
3368
3369         var correctFrame = function(tween) {
3370             var frames = tween.totalFrames;
3371             var frame = tween.currentFrame;
3372             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3373             var elapsed = (new Date() - tween.getStartTime());
3374             var tweak = 0;
3375
3376             if (elapsed < tween.duration * 1000) {
3377                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3378             } else {
3379                 tweak = frames - (frame + 1);
3380             }
3381             if (tweak > 0 && isFinite(tweak)) {
3382                 if (tween.currentFrame + tweak >= frames) {
3383                     tweak = frames - (frame + 1);
3384                 }
3385
3386                 tween.currentFrame += tweak;
3387             }
3388         };
3389     };/*
3390  * Portions of this file are based on pieces of Yahoo User Interface Library
3391  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3392  * YUI licensed under the BSD License:
3393  * http://developer.yahoo.net/yui/license.txt
3394  * <script type="text/javascript">
3395  *
3396  */
3397 Roo.lib.Bezier = new function() {
3398
3399         this.getPosition = function(points, t) {
3400             var n = points.length;
3401             var tmp = [];
3402
3403             for (var i = 0; i < n; ++i) {
3404                 tmp[i] = [points[i][0], points[i][1]];
3405             }
3406
3407             for (var j = 1; j < n; ++j) {
3408                 for (i = 0; i < n - j; ++i) {
3409                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3410                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3411                 }
3412             }
3413
3414             return [ tmp[0][0], tmp[0][1] ];
3415
3416         };
3417     };/*
3418  * Portions of this file are based on pieces of Yahoo User Interface Library
3419  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3420  * YUI licensed under the BSD License:
3421  * http://developer.yahoo.net/yui/license.txt
3422  * <script type="text/javascript">
3423  *
3424  */
3425 (function() {
3426
3427     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3428         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3429     };
3430
3431     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3432
3433     var fly = Roo.lib.AnimBase.fly;
3434     var Y = Roo.lib;
3435     var superclass = Y.ColorAnim.superclass;
3436     var proto = Y.ColorAnim.prototype;
3437
3438     proto.toString = function() {
3439         var el = this.getEl();
3440         var id = el.id || el.tagName;
3441         return ("ColorAnim " + id);
3442     };
3443
3444     proto.patterns.color = /color$/i;
3445     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3446     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3447     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3448     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3449
3450
3451     proto.parseColor = function(s) {
3452         if (s.length == 3) {
3453             return s;
3454         }
3455
3456         var c = this.patterns.hex.exec(s);
3457         if (c && c.length == 4) {
3458             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3459         }
3460
3461         c = this.patterns.rgb.exec(s);
3462         if (c && c.length == 4) {
3463             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3464         }
3465
3466         c = this.patterns.hex3.exec(s);
3467         if (c && c.length == 4) {
3468             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3469         }
3470
3471         return null;
3472     };
3473     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3474     proto.getAttribute = function(attr) {
3475         var el = this.getEl();
3476         if (this.patterns.color.test(attr)) {
3477             var val = fly(el).getStyle(attr);
3478
3479             if (this.patterns.transparent.test(val)) {
3480                 var parent = el.parentNode;
3481                 val = fly(parent).getStyle(attr);
3482
3483                 while (parent && this.patterns.transparent.test(val)) {
3484                     parent = parent.parentNode;
3485                     val = fly(parent).getStyle(attr);
3486                     if (parent.tagName.toUpperCase() == 'HTML') {
3487                         val = '#fff';
3488                     }
3489                 }
3490             }
3491         } else {
3492             val = superclass.getAttribute.call(this, attr);
3493         }
3494
3495         return val;
3496     };
3497     proto.getAttribute = function(attr) {
3498         var el = this.getEl();
3499         if (this.patterns.color.test(attr)) {
3500             var val = fly(el).getStyle(attr);
3501
3502             if (this.patterns.transparent.test(val)) {
3503                 var parent = el.parentNode;
3504                 val = fly(parent).getStyle(attr);
3505
3506                 while (parent && this.patterns.transparent.test(val)) {
3507                     parent = parent.parentNode;
3508                     val = fly(parent).getStyle(attr);
3509                     if (parent.tagName.toUpperCase() == 'HTML') {
3510                         val = '#fff';
3511                     }
3512                 }
3513             }
3514         } else {
3515             val = superclass.getAttribute.call(this, attr);
3516         }
3517
3518         return val;
3519     };
3520
3521     proto.doMethod = function(attr, start, end) {
3522         var val;
3523
3524         if (this.patterns.color.test(attr)) {
3525             val = [];
3526             for (var i = 0, len = start.length; i < len; ++i) {
3527                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3528             }
3529
3530             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3531         }
3532         else {
3533             val = superclass.doMethod.call(this, attr, start, end);
3534         }
3535
3536         return val;
3537     };
3538
3539     proto.setRuntimeAttribute = function(attr) {
3540         superclass.setRuntimeAttribute.call(this, attr);
3541
3542         if (this.patterns.color.test(attr)) {
3543             var attributes = this.attributes;
3544             var start = this.parseColor(this.runtimeAttributes[attr].start);
3545             var end = this.parseColor(this.runtimeAttributes[attr].end);
3546
3547             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3548                 end = this.parseColor(attributes[attr].by);
3549
3550                 for (var i = 0, len = start.length; i < len; ++i) {
3551                     end[i] = start[i] + end[i];
3552                 }
3553             }
3554
3555             this.runtimeAttributes[attr].start = start;
3556             this.runtimeAttributes[attr].end = end;
3557         }
3558     };
3559 })();
3560
3561 /*
3562  * Portions of this file are based on pieces of Yahoo User Interface Library
3563  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3564  * YUI licensed under the BSD License:
3565  * http://developer.yahoo.net/yui/license.txt
3566  * <script type="text/javascript">
3567  *
3568  */
3569 Roo.lib.Easing = {
3570
3571
3572     easeNone: function (t, b, c, d) {
3573         return c * t / d + b;
3574     },
3575
3576
3577     easeIn: function (t, b, c, d) {
3578         return c * (t /= d) * t + b;
3579     },
3580
3581
3582     easeOut: function (t, b, c, d) {
3583         return -c * (t /= d) * (t - 2) + b;
3584     },
3585
3586
3587     easeBoth: function (t, b, c, d) {
3588         if ((t /= d / 2) < 1) {
3589             return c / 2 * t * t + b;
3590         }
3591
3592         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3593     },
3594
3595
3596     easeInStrong: function (t, b, c, d) {
3597         return c * (t /= d) * t * t * t + b;
3598     },
3599
3600
3601     easeOutStrong: function (t, b, c, d) {
3602         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3603     },
3604
3605
3606     easeBothStrong: function (t, b, c, d) {
3607         if ((t /= d / 2) < 1) {
3608             return c / 2 * t * t * t * t + b;
3609         }
3610
3611         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3612     },
3613
3614
3615
3616     elasticIn: function (t, b, c, d, a, p) {
3617         if (t == 0) {
3618             return b;
3619         }
3620         if ((t /= d) == 1) {
3621             return b + c;
3622         }
3623         if (!p) {
3624             p = d * .3;
3625         }
3626
3627         if (!a || a < Math.abs(c)) {
3628             a = c;
3629             var s = p / 4;
3630         }
3631         else {
3632             var s = p / (2 * Math.PI) * Math.asin(c / a);
3633         }
3634
3635         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3636     },
3637
3638
3639     elasticOut: function (t, b, c, d, a, p) {
3640         if (t == 0) {
3641             return b;
3642         }
3643         if ((t /= d) == 1) {
3644             return b + c;
3645         }
3646         if (!p) {
3647             p = d * .3;
3648         }
3649
3650         if (!a || a < Math.abs(c)) {
3651             a = c;
3652             var s = p / 4;
3653         }
3654         else {
3655             var s = p / (2 * Math.PI) * Math.asin(c / a);
3656         }
3657
3658         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3659     },
3660
3661
3662     elasticBoth: function (t, b, c, d, a, p) {
3663         if (t == 0) {
3664             return b;
3665         }
3666
3667         if ((t /= d / 2) == 2) {
3668             return b + c;
3669         }
3670
3671         if (!p) {
3672             p = d * (.3 * 1.5);
3673         }
3674
3675         if (!a || a < Math.abs(c)) {
3676             a = c;
3677             var s = p / 4;
3678         }
3679         else {
3680             var s = p / (2 * Math.PI) * Math.asin(c / a);
3681         }
3682
3683         if (t < 1) {
3684             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3685                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3686         }
3687         return a * Math.pow(2, -10 * (t -= 1)) *
3688                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3689     },
3690
3691
3692
3693     backIn: function (t, b, c, d, s) {
3694         if (typeof s == 'undefined') {
3695             s = 1.70158;
3696         }
3697         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3698     },
3699
3700
3701     backOut: function (t, b, c, d, s) {
3702         if (typeof s == 'undefined') {
3703             s = 1.70158;
3704         }
3705         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3706     },
3707
3708
3709     backBoth: function (t, b, c, d, s) {
3710         if (typeof s == 'undefined') {
3711             s = 1.70158;
3712         }
3713
3714         if ((t /= d / 2 ) < 1) {
3715             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3716         }
3717         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3718     },
3719
3720
3721     bounceIn: function (t, b, c, d) {
3722         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3723     },
3724
3725
3726     bounceOut: function (t, b, c, d) {
3727         if ((t /= d) < (1 / 2.75)) {
3728             return c * (7.5625 * t * t) + b;
3729         } else if (t < (2 / 2.75)) {
3730             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3731         } else if (t < (2.5 / 2.75)) {
3732             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3733         }
3734         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3735     },
3736
3737
3738     bounceBoth: function (t, b, c, d) {
3739         if (t < d / 2) {
3740             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3741         }
3742         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3743     }
3744 };/*
3745  * Portions of this file are based on pieces of Yahoo User Interface Library
3746  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3747  * YUI licensed under the BSD License:
3748  * http://developer.yahoo.net/yui/license.txt
3749  * <script type="text/javascript">
3750  *
3751  */
3752     (function() {
3753         Roo.lib.Motion = function(el, attributes, duration, method) {
3754             if (el) {
3755                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3756             }
3757         };
3758
3759         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3760
3761
3762         var Y = Roo.lib;
3763         var superclass = Y.Motion.superclass;
3764         var proto = Y.Motion.prototype;
3765
3766         proto.toString = function() {
3767             var el = this.getEl();
3768             var id = el.id || el.tagName;
3769             return ("Motion " + id);
3770         };
3771
3772         proto.patterns.points = /^points$/i;
3773
3774         proto.setAttribute = function(attr, val, unit) {
3775             if (this.patterns.points.test(attr)) {
3776                 unit = unit || 'px';
3777                 superclass.setAttribute.call(this, 'left', val[0], unit);
3778                 superclass.setAttribute.call(this, 'top', val[1], unit);
3779             } else {
3780                 superclass.setAttribute.call(this, attr, val, unit);
3781             }
3782         };
3783
3784         proto.getAttribute = function(attr) {
3785             if (this.patterns.points.test(attr)) {
3786                 var val = [
3787                         superclass.getAttribute.call(this, 'left'),
3788                         superclass.getAttribute.call(this, 'top')
3789                         ];
3790             } else {
3791                 val = superclass.getAttribute.call(this, attr);
3792             }
3793
3794             return val;
3795         };
3796
3797         proto.doMethod = function(attr, start, end) {
3798             var val = null;
3799
3800             if (this.patterns.points.test(attr)) {
3801                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3802                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3803             } else {
3804                 val = superclass.doMethod.call(this, attr, start, end);
3805             }
3806             return val;
3807         };
3808
3809         proto.setRuntimeAttribute = function(attr) {
3810             if (this.patterns.points.test(attr)) {
3811                 var el = this.getEl();
3812                 var attributes = this.attributes;
3813                 var start;
3814                 var control = attributes['points']['control'] || [];
3815                 var end;
3816                 var i, len;
3817
3818                 if (control.length > 0 && !(control[0] instanceof Array)) {
3819                     control = [control];
3820                 } else {
3821                     var tmp = [];
3822                     for (i = 0,len = control.length; i < len; ++i) {
3823                         tmp[i] = control[i];
3824                     }
3825                     control = tmp;
3826                 }
3827
3828                 Roo.fly(el).position();
3829
3830                 if (isset(attributes['points']['from'])) {
3831                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3832                 }
3833                 else {
3834                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3835                 }
3836
3837                 start = this.getAttribute('points');
3838
3839
3840                 if (isset(attributes['points']['to'])) {
3841                     end = translateValues.call(this, attributes['points']['to'], start);
3842
3843                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3844                     for (i = 0,len = control.length; i < len; ++i) {
3845                         control[i] = translateValues.call(this, control[i], start);
3846                     }
3847
3848
3849                 } else if (isset(attributes['points']['by'])) {
3850                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3851
3852                     for (i = 0,len = control.length; i < len; ++i) {
3853                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3854                     }
3855                 }
3856
3857                 this.runtimeAttributes[attr] = [start];
3858
3859                 if (control.length > 0) {
3860                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3861                 }
3862
3863                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3864             }
3865             else {
3866                 superclass.setRuntimeAttribute.call(this, attr);
3867             }
3868         };
3869
3870         var translateValues = function(val, start) {
3871             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3872             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3873
3874             return val;
3875         };
3876
3877         var isset = function(prop) {
3878             return (typeof prop !== 'undefined');
3879         };
3880     })();
3881 /*
3882  * Portions of this file are based on pieces of Yahoo User Interface Library
3883  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3884  * YUI licensed under the BSD License:
3885  * http://developer.yahoo.net/yui/license.txt
3886  * <script type="text/javascript">
3887  *
3888  */
3889     (function() {
3890         Roo.lib.Scroll = function(el, attributes, duration, method) {
3891             if (el) {
3892                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3893             }
3894         };
3895
3896         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3897
3898
3899         var Y = Roo.lib;
3900         var superclass = Y.Scroll.superclass;
3901         var proto = Y.Scroll.prototype;
3902
3903         proto.toString = function() {
3904             var el = this.getEl();
3905             var id = el.id || el.tagName;
3906             return ("Scroll " + id);
3907         };
3908
3909         proto.doMethod = function(attr, start, end) {
3910             var val = null;
3911
3912             if (attr == 'scroll') {
3913                 val = [
3914                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3915                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3916                         ];
3917
3918             } else {
3919                 val = superclass.doMethod.call(this, attr, start, end);
3920             }
3921             return val;
3922         };
3923
3924         proto.getAttribute = function(attr) {
3925             var val = null;
3926             var el = this.getEl();
3927
3928             if (attr == 'scroll') {
3929                 val = [ el.scrollLeft, el.scrollTop ];
3930             } else {
3931                 val = superclass.getAttribute.call(this, attr);
3932             }
3933
3934             return val;
3935         };
3936
3937         proto.setAttribute = function(attr, val, unit) {
3938             var el = this.getEl();
3939
3940             if (attr == 'scroll') {
3941                 el.scrollLeft = val[0];
3942                 el.scrollTop = val[1];
3943             } else {
3944                 superclass.setAttribute.call(this, attr, val, unit);
3945             }
3946         };
3947     })();
3948 /*
3949  * Based on:
3950  * Ext JS Library 1.1.1
3951  * Copyright(c) 2006-2007, Ext JS, LLC.
3952  *
3953  * Originally Released Under LGPL - original licence link has changed is not relivant.
3954  *
3955  * Fork - LGPL
3956  * <script type="text/javascript">
3957  */
3958  
3959
3960 /**
3961  * @class Roo.DomHelper
3962  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3963  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
3964  * @singleton
3965  */
3966 Roo.DomHelper = function(){
3967     var tempTableEl = null;
3968     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3969     var tableRe = /^table|tbody|tr|td$/i;
3970     var xmlns = {};
3971     // build as innerHTML where available
3972     /** @ignore */
3973     var createHtml = function(o){
3974         if(typeof o == 'string'){
3975             return o;
3976         }
3977         var b = "";
3978         if(!o.tag){
3979             o.tag = "div";
3980         }
3981         b += "<" + o.tag;
3982         for(var attr in o){
3983             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3984             if(attr == "style"){
3985                 var s = o["style"];
3986                 if(typeof s == "function"){
3987                     s = s.call();
3988                 }
3989                 if(typeof s == "string"){
3990                     b += ' style="' + s + '"';
3991                 }else if(typeof s == "object"){
3992                     b += ' style="';
3993                     for(var key in s){
3994                         if(typeof s[key] != "function"){
3995                             b += key + ":" + s[key] + ";";
3996                         }
3997                     }
3998                     b += '"';
3999                 }
4000             }else{
4001                 if(attr == "cls"){
4002                     b += ' class="' + o["cls"] + '"';
4003                 }else if(attr == "htmlFor"){
4004                     b += ' for="' + o["htmlFor"] + '"';
4005                 }else{
4006                     b += " " + attr + '="' + o[attr] + '"';
4007                 }
4008             }
4009         }
4010         if(emptyTags.test(o.tag)){
4011             b += "/>";
4012         }else{
4013             b += ">";
4014             var cn = o.children || o.cn;
4015             if(cn){
4016                 //http://bugs.kde.org/show_bug.cgi?id=71506
4017                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4018                     for(var i = 0, len = cn.length; i < len; i++) {
4019                         b += createHtml(cn[i], b);
4020                     }
4021                 }else{
4022                     b += createHtml(cn, b);
4023                 }
4024             }
4025             if(o.html){
4026                 b += o.html;
4027             }
4028             b += "</" + o.tag + ">";
4029         }
4030         return b;
4031     };
4032
4033     // build as dom
4034     /** @ignore */
4035     var createDom = function(o, parentNode){
4036          
4037         // defininition craeted..
4038         var ns = false;
4039         if (o.ns && o.ns != 'html') {
4040                
4041             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4042                 xmlns[o.ns] = o.xmlns;
4043                 ns = o.xmlns;
4044             }
4045             if (typeof(xmlns[o.ns]) == 'undefined') {
4046                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4047             }
4048             ns = xmlns[o.ns];
4049         }
4050         
4051         
4052         if (typeof(o) == 'string') {
4053             return parentNode.appendChild(document.createTextNode(o));
4054         }
4055         o.tag = o.tag || div;
4056         if (o.ns && Roo.isIE) {
4057             ns = false;
4058             o.tag = o.ns + ':' + o.tag;
4059             
4060         }
4061         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4062         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4063         for(var attr in o){
4064             
4065             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4066                     attr == "style" || typeof o[attr] == "function") continue;
4067                     
4068             if(attr=="cls" && Roo.isIE){
4069                 el.className = o["cls"];
4070             }else{
4071                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4072                 else el[attr] = o[attr];
4073             }
4074         }
4075         Roo.DomHelper.applyStyles(el, o.style);
4076         var cn = o.children || o.cn;
4077         if(cn){
4078             //http://bugs.kde.org/show_bug.cgi?id=71506
4079              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4080                 for(var i = 0, len = cn.length; i < len; i++) {
4081                     createDom(cn[i], el);
4082                 }
4083             }else{
4084                 createDom(cn, el);
4085             }
4086         }
4087         if(o.html){
4088             el.innerHTML = o.html;
4089         }
4090         if(parentNode){
4091            parentNode.appendChild(el);
4092         }
4093         return el;
4094     };
4095
4096     var ieTable = function(depth, s, h, e){
4097         tempTableEl.innerHTML = [s, h, e].join('');
4098         var i = -1, el = tempTableEl;
4099         while(++i < depth){
4100             el = el.firstChild;
4101         }
4102         return el;
4103     };
4104
4105     // kill repeat to save bytes
4106     var ts = '<table>',
4107         te = '</table>',
4108         tbs = ts+'<tbody>',
4109         tbe = '</tbody>'+te,
4110         trs = tbs + '<tr>',
4111         tre = '</tr>'+tbe;
4112
4113     /**
4114      * @ignore
4115      * Nasty code for IE's broken table implementation
4116      */
4117     var insertIntoTable = function(tag, where, el, html){
4118         if(!tempTableEl){
4119             tempTableEl = document.createElement('div');
4120         }
4121         var node;
4122         var before = null;
4123         if(tag == 'td'){
4124             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4125                 return;
4126             }
4127             if(where == 'beforebegin'){
4128                 before = el;
4129                 el = el.parentNode;
4130             } else{
4131                 before = el.nextSibling;
4132                 el = el.parentNode;
4133             }
4134             node = ieTable(4, trs, html, tre);
4135         }
4136         else if(tag == 'tr'){
4137             if(where == 'beforebegin'){
4138                 before = el;
4139                 el = el.parentNode;
4140                 node = ieTable(3, tbs, html, tbe);
4141             } else if(where == 'afterend'){
4142                 before = el.nextSibling;
4143                 el = el.parentNode;
4144                 node = ieTable(3, tbs, html, tbe);
4145             } else{ // INTO a TR
4146                 if(where == 'afterbegin'){
4147                     before = el.firstChild;
4148                 }
4149                 node = ieTable(4, trs, html, tre);
4150             }
4151         } else if(tag == 'tbody'){
4152             if(where == 'beforebegin'){
4153                 before = el;
4154                 el = el.parentNode;
4155                 node = ieTable(2, ts, html, te);
4156             } else if(where == 'afterend'){
4157                 before = el.nextSibling;
4158                 el = el.parentNode;
4159                 node = ieTable(2, ts, html, te);
4160             } else{
4161                 if(where == 'afterbegin'){
4162                     before = el.firstChild;
4163                 }
4164                 node = ieTable(3, tbs, html, tbe);
4165             }
4166         } else{ // TABLE
4167             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4168                 return;
4169             }
4170             if(where == 'afterbegin'){
4171                 before = el.firstChild;
4172             }
4173             node = ieTable(2, ts, html, te);
4174         }
4175         el.insertBefore(node, before);
4176         return node;
4177     };
4178
4179     return {
4180     /** True to force the use of DOM instead of html fragments @type Boolean */
4181     useDom : false,
4182
4183     /**
4184      * Returns the markup for the passed Element(s) config
4185      * @param {Object} o The Dom object spec (and children)
4186      * @return {String}
4187      */
4188     markup : function(o){
4189         return createHtml(o);
4190     },
4191
4192     /**
4193      * Applies a style specification to an element
4194      * @param {String/HTMLElement} el The element to apply styles to
4195      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4196      * a function which returns such a specification.
4197      */
4198     applyStyles : function(el, styles){
4199         if(styles){
4200            el = Roo.fly(el);
4201            if(typeof styles == "string"){
4202                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4203                var matches;
4204                while ((matches = re.exec(styles)) != null){
4205                    el.setStyle(matches[1], matches[2]);
4206                }
4207            }else if (typeof styles == "object"){
4208                for (var style in styles){
4209                   el.setStyle(style, styles[style]);
4210                }
4211            }else if (typeof styles == "function"){
4212                 Roo.DomHelper.applyStyles(el, styles.call());
4213            }
4214         }
4215     },
4216
4217     /**
4218      * Inserts an HTML fragment into the Dom
4219      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4220      * @param {HTMLElement} el The context element
4221      * @param {String} html The HTML fragmenet
4222      * @return {HTMLElement} The new node
4223      */
4224     insertHtml : function(where, el, html){
4225         where = where.toLowerCase();
4226         if(el.insertAdjacentHTML){
4227             if(tableRe.test(el.tagName)){
4228                 var rs;
4229                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4230                     return rs;
4231                 }
4232             }
4233             switch(where){
4234                 case "beforebegin":
4235                     el.insertAdjacentHTML('BeforeBegin', html);
4236                     return el.previousSibling;
4237                 case "afterbegin":
4238                     el.insertAdjacentHTML('AfterBegin', html);
4239                     return el.firstChild;
4240                 case "beforeend":
4241                     el.insertAdjacentHTML('BeforeEnd', html);
4242                     return el.lastChild;
4243                 case "afterend":
4244                     el.insertAdjacentHTML('AfterEnd', html);
4245                     return el.nextSibling;
4246             }
4247             throw 'Illegal insertion point -> "' + where + '"';
4248         }
4249         var range = el.ownerDocument.createRange();
4250         var frag;
4251         switch(where){
4252              case "beforebegin":
4253                 range.setStartBefore(el);
4254                 frag = range.createContextualFragment(html);
4255                 el.parentNode.insertBefore(frag, el);
4256                 return el.previousSibling;
4257              case "afterbegin":
4258                 if(el.firstChild){
4259                     range.setStartBefore(el.firstChild);
4260                     frag = range.createContextualFragment(html);
4261                     el.insertBefore(frag, el.firstChild);
4262                     return el.firstChild;
4263                 }else{
4264                     el.innerHTML = html;
4265                     return el.firstChild;
4266                 }
4267             case "beforeend":
4268                 if(el.lastChild){
4269                     range.setStartAfter(el.lastChild);
4270                     frag = range.createContextualFragment(html);
4271                     el.appendChild(frag);
4272                     return el.lastChild;
4273                 }else{
4274                     el.innerHTML = html;
4275                     return el.lastChild;
4276                 }
4277             case "afterend":
4278                 range.setStartAfter(el);
4279                 frag = range.createContextualFragment(html);
4280                 el.parentNode.insertBefore(frag, el.nextSibling);
4281                 return el.nextSibling;
4282             }
4283             throw 'Illegal insertion point -> "' + where + '"';
4284     },
4285
4286     /**
4287      * Creates new Dom element(s) and inserts them before el
4288      * @param {String/HTMLElement/Element} el The context element
4289      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4290      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4291      * @return {HTMLElement/Roo.Element} The new node
4292      */
4293     insertBefore : function(el, o, returnElement){
4294         return this.doInsert(el, o, returnElement, "beforeBegin");
4295     },
4296
4297     /**
4298      * Creates new Dom element(s) and inserts them after el
4299      * @param {String/HTMLElement/Element} el The context element
4300      * @param {Object} o The Dom object spec (and children)
4301      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4302      * @return {HTMLElement/Roo.Element} The new node
4303      */
4304     insertAfter : function(el, o, returnElement){
4305         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4306     },
4307
4308     /**
4309      * Creates new Dom element(s) and inserts them as the first child of el
4310      * @param {String/HTMLElement/Element} el The context element
4311      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4312      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4313      * @return {HTMLElement/Roo.Element} The new node
4314      */
4315     insertFirst : function(el, o, returnElement){
4316         return this.doInsert(el, o, returnElement, "afterBegin");
4317     },
4318
4319     // private
4320     doInsert : function(el, o, returnElement, pos, sibling){
4321         el = Roo.getDom(el);
4322         var newNode;
4323         if(this.useDom || o.ns){
4324             newNode = createDom(o, null);
4325             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4326         }else{
4327             var html = createHtml(o);
4328             newNode = this.insertHtml(pos, el, html);
4329         }
4330         return returnElement ? Roo.get(newNode, true) : newNode;
4331     },
4332
4333     /**
4334      * Creates new Dom element(s) and appends them to el
4335      * @param {String/HTMLElement/Element} el The context element
4336      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4337      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4338      * @return {HTMLElement/Roo.Element} The new node
4339      */
4340     append : function(el, o, returnElement){
4341         el = Roo.getDom(el);
4342         var newNode;
4343         if(this.useDom || o.ns){
4344             newNode = createDom(o, null);
4345             el.appendChild(newNode);
4346         }else{
4347             var html = createHtml(o);
4348             newNode = this.insertHtml("beforeEnd", el, html);
4349         }
4350         return returnElement ? Roo.get(newNode, true) : newNode;
4351     },
4352
4353     /**
4354      * Creates new Dom element(s) and overwrites the contents of el with them
4355      * @param {String/HTMLElement/Element} el The context element
4356      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4357      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4358      * @return {HTMLElement/Roo.Element} The new node
4359      */
4360     overwrite : function(el, o, returnElement){
4361         el = Roo.getDom(el);
4362         if (o.ns) {
4363           
4364             while (el.childNodes.length) {
4365                 el.removeChild(el.firstChild);
4366             }
4367             createDom(o, el);
4368         } else {
4369             el.innerHTML = createHtml(o);   
4370         }
4371         
4372         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4373     },
4374
4375     /**
4376      * Creates a new Roo.DomHelper.Template from the Dom object spec
4377      * @param {Object} o The Dom object spec (and children)
4378      * @return {Roo.DomHelper.Template} The new template
4379      */
4380     createTemplate : function(o){
4381         var html = createHtml(o);
4382         return new Roo.Template(html);
4383     }
4384     };
4385 }();
4386 /*
4387  * Based on:
4388  * Ext JS Library 1.1.1
4389  * Copyright(c) 2006-2007, Ext JS, LLC.
4390  *
4391  * Originally Released Under LGPL - original licence link has changed is not relivant.
4392  *
4393  * Fork - LGPL
4394  * <script type="text/javascript">
4395  */
4396  
4397 /**
4398 * @class Roo.Template
4399 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4400 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4401 * Usage:
4402 <pre><code>
4403 var t = new Roo.Template({
4404     html :  '&lt;div name="{id}"&gt;' + 
4405         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4406         '&lt;/div&gt;',
4407     myformat: function (value, allValues) {
4408         return 'XX' + value;
4409     }
4410 });
4411 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4412 </code></pre>
4413 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4414 * @constructor
4415 * @param {Object} cfg - Configuration object.
4416 */
4417 Roo.Template = function(cfg){
4418     // BC!
4419     if(cfg instanceof Array){
4420         cfg = cfg.join("");
4421     }else if(arguments.length > 1){
4422         cfg = Array.prototype.join.call(arguments, "");
4423     }
4424     
4425     
4426     if (typeof(cfg) == 'object') {
4427         Roo.apply(this,cfg)
4428     } else {
4429         // bc
4430         this.html = cfg;
4431     }
4432     
4433     
4434 };
4435 Roo.Template.prototype = {
4436     
4437     /**
4438      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4439      */
4440     html : '',
4441     /**
4442      * Returns an HTML fragment of this template with the specified values applied.
4443      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4444      * @return {String} The HTML fragment
4445      */
4446     applyTemplate : function(values){
4447         try {
4448             
4449             if(this.compiled){
4450                 return this.compiled(values);
4451             }
4452             var useF = this.disableFormats !== true;
4453             var fm = Roo.util.Format, tpl = this;
4454             var fn = function(m, name, format, args){
4455                 if(format && useF){
4456                     if(format.substr(0, 5) == "this."){
4457                         return tpl.call(format.substr(5), values[name], values);
4458                     }else{
4459                         if(args){
4460                             // quoted values are required for strings in compiled templates, 
4461                             // but for non compiled we need to strip them
4462                             // quoted reversed for jsmin
4463                             var re = /^\s*['"](.*)["']\s*$/;
4464                             args = args.split(',');
4465                             for(var i = 0, len = args.length; i < len; i++){
4466                                 args[i] = args[i].replace(re, "$1");
4467                             }
4468                             args = [values[name]].concat(args);
4469                         }else{
4470                             args = [values[name]];
4471                         }
4472                         return fm[format].apply(fm, args);
4473                     }
4474                 }else{
4475                     return values[name] !== undefined ? values[name] : "";
4476                 }
4477             };
4478             return this.html.replace(this.re, fn);
4479         } catch (e) {
4480             Roo.log(e);
4481             throw e;
4482         }
4483          
4484     },
4485     
4486     /**
4487      * Sets the HTML used as the template and optionally compiles it.
4488      * @param {String} html
4489      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4490      * @return {Roo.Template} this
4491      */
4492     set : function(html, compile){
4493         this.html = html;
4494         this.compiled = null;
4495         if(compile){
4496             this.compile();
4497         }
4498         return this;
4499     },
4500     
4501     /**
4502      * True to disable format functions (defaults to false)
4503      * @type Boolean
4504      */
4505     disableFormats : false,
4506     
4507     /**
4508     * The regular expression used to match template variables 
4509     * @type RegExp
4510     * @property 
4511     */
4512     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4513     
4514     /**
4515      * Compiles the template into an internal function, eliminating the RegEx overhead.
4516      * @return {Roo.Template} this
4517      */
4518     compile : function(){
4519         var fm = Roo.util.Format;
4520         var useF = this.disableFormats !== true;
4521         var sep = Roo.isGecko ? "+" : ",";
4522         var fn = function(m, name, format, args){
4523             if(format && useF){
4524                 args = args ? ',' + args : "";
4525                 if(format.substr(0, 5) != "this."){
4526                     format = "fm." + format + '(';
4527                 }else{
4528                     format = 'this.call("'+ format.substr(5) + '", ';
4529                     args = ", values";
4530                 }
4531             }else{
4532                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4533             }
4534             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4535         };
4536         var body;
4537         // branched to use + in gecko and [].join() in others
4538         if(Roo.isGecko){
4539             body = "this.compiled = function(values){ return '" +
4540                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4541                     "';};";
4542         }else{
4543             body = ["this.compiled = function(values){ return ['"];
4544             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4545             body.push("'].join('');};");
4546             body = body.join('');
4547         }
4548         /**
4549          * eval:var:values
4550          * eval:var:fm
4551          */
4552         eval(body);
4553         return this;
4554     },
4555     
4556     // private function used to call members
4557     call : function(fnName, value, allValues){
4558         return this[fnName](value, allValues);
4559     },
4560     
4561     /**
4562      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4563      * @param {String/HTMLElement/Roo.Element} el The context element
4564      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4565      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4566      * @return {HTMLElement/Roo.Element} The new node or Element
4567      */
4568     insertFirst: function(el, values, returnElement){
4569         return this.doInsert('afterBegin', el, values, returnElement);
4570     },
4571
4572     /**
4573      * Applies the supplied values to the template and inserts the new node(s) before el.
4574      * @param {String/HTMLElement/Roo.Element} el The context element
4575      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4576      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4577      * @return {HTMLElement/Roo.Element} The new node or Element
4578      */
4579     insertBefore: function(el, values, returnElement){
4580         return this.doInsert('beforeBegin', el, values, returnElement);
4581     },
4582
4583     /**
4584      * Applies the supplied values to the template and inserts the new node(s) after el.
4585      * @param {String/HTMLElement/Roo.Element} el The context element
4586      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4587      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4588      * @return {HTMLElement/Roo.Element} The new node or Element
4589      */
4590     insertAfter : function(el, values, returnElement){
4591         return this.doInsert('afterEnd', el, values, returnElement);
4592     },
4593     
4594     /**
4595      * Applies the supplied values to the template and appends the new node(s) to el.
4596      * @param {String/HTMLElement/Roo.Element} el The context element
4597      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4598      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4599      * @return {HTMLElement/Roo.Element} The new node or Element
4600      */
4601     append : function(el, values, returnElement){
4602         return this.doInsert('beforeEnd', el, values, returnElement);
4603     },
4604
4605     doInsert : function(where, el, values, returnEl){
4606         el = Roo.getDom(el);
4607         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4608         return returnEl ? Roo.get(newNode, true) : newNode;
4609     },
4610
4611     /**
4612      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4613      * @param {String/HTMLElement/Roo.Element} el The context element
4614      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4615      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4616      * @return {HTMLElement/Roo.Element} The new node or Element
4617      */
4618     overwrite : function(el, values, returnElement){
4619         el = Roo.getDom(el);
4620         el.innerHTML = this.applyTemplate(values);
4621         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4622     }
4623 };
4624 /**
4625  * Alias for {@link #applyTemplate}
4626  * @method
4627  */
4628 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4629
4630 // backwards compat
4631 Roo.DomHelper.Template = Roo.Template;
4632
4633 /**
4634  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4635  * @param {String/HTMLElement} el A DOM element or its id
4636  * @returns {Roo.Template} The created template
4637  * @static
4638  */
4639 Roo.Template.from = function(el){
4640     el = Roo.getDom(el);
4641     return new Roo.Template(el.value || el.innerHTML);
4642 };/*
4643  * Based on:
4644  * Ext JS Library 1.1.1
4645  * Copyright(c) 2006-2007, Ext JS, LLC.
4646  *
4647  * Originally Released Under LGPL - original licence link has changed is not relivant.
4648  *
4649  * Fork - LGPL
4650  * <script type="text/javascript">
4651  */
4652  
4653
4654 /*
4655  * This is code is also distributed under MIT license for use
4656  * with jQuery and prototype JavaScript libraries.
4657  */
4658 /**
4659  * @class Roo.DomQuery
4660 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4661 <p>
4662 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4663
4664 <p>
4665 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4666 </p>
4667 <h4>Element Selectors:</h4>
4668 <ul class="list">
4669     <li> <b>*</b> any element</li>
4670     <li> <b>E</b> an element with the tag E</li>
4671     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4672     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4673     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4674     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4675 </ul>
4676 <h4>Attribute Selectors:</h4>
4677 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4678 <ul class="list">
4679     <li> <b>E[foo]</b> has an attribute "foo"</li>
4680     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4681     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4682     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4683     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4684     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4685     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4686 </ul>
4687 <h4>Pseudo Classes:</h4>
4688 <ul class="list">
4689     <li> <b>E:first-child</b> E is the first child of its parent</li>
4690     <li> <b>E:last-child</b> E is the last child of its parent</li>
4691     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4692     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4693     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4694     <li> <b>E:only-child</b> E is the only child of its parent</li>
4695     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4696     <li> <b>E:first</b> the first E in the resultset</li>
4697     <li> <b>E:last</b> the last E in the resultset</li>
4698     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4699     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4700     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4701     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4702     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4703     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4704     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4705     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4706     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4707 </ul>
4708 <h4>CSS Value Selectors:</h4>
4709 <ul class="list">
4710     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4711     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4712     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4713     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4714     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4715     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4716 </ul>
4717  * @singleton
4718  */
4719 Roo.DomQuery = function(){
4720     var cache = {}, simpleCache = {}, valueCache = {};
4721     var nonSpace = /\S/;
4722     var trimRe = /^\s+|\s+$/g;
4723     var tplRe = /\{(\d+)\}/g;
4724     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4725     var tagTokenRe = /^(#)?([\w-\*]+)/;
4726     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4727
4728     function child(p, index){
4729         var i = 0;
4730         var n = p.firstChild;
4731         while(n){
4732             if(n.nodeType == 1){
4733                if(++i == index){
4734                    return n;
4735                }
4736             }
4737             n = n.nextSibling;
4738         }
4739         return null;
4740     };
4741
4742     function next(n){
4743         while((n = n.nextSibling) && n.nodeType != 1);
4744         return n;
4745     };
4746
4747     function prev(n){
4748         while((n = n.previousSibling) && n.nodeType != 1);
4749         return n;
4750     };
4751
4752     function children(d){
4753         var n = d.firstChild, ni = -1;
4754             while(n){
4755                 var nx = n.nextSibling;
4756                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4757                     d.removeChild(n);
4758                 }else{
4759                     n.nodeIndex = ++ni;
4760                 }
4761                 n = nx;
4762             }
4763             return this;
4764         };
4765
4766     function byClassName(c, a, v){
4767         if(!v){
4768             return c;
4769         }
4770         var r = [], ri = -1, cn;
4771         for(var i = 0, ci; ci = c[i]; i++){
4772             if((' '+ci.className+' ').indexOf(v) != -1){
4773                 r[++ri] = ci;
4774             }
4775         }
4776         return r;
4777     };
4778
4779     function attrValue(n, attr){
4780         if(!n.tagName && typeof n.length != "undefined"){
4781             n = n[0];
4782         }
4783         if(!n){
4784             return null;
4785         }
4786         if(attr == "for"){
4787             return n.htmlFor;
4788         }
4789         if(attr == "class" || attr == "className"){
4790             return n.className;
4791         }
4792         return n.getAttribute(attr) || n[attr];
4793
4794     };
4795
4796     function getNodes(ns, mode, tagName){
4797         var result = [], ri = -1, cs;
4798         if(!ns){
4799             return result;
4800         }
4801         tagName = tagName || "*";
4802         if(typeof ns.getElementsByTagName != "undefined"){
4803             ns = [ns];
4804         }
4805         if(!mode){
4806             for(var i = 0, ni; ni = ns[i]; i++){
4807                 cs = ni.getElementsByTagName(tagName);
4808                 for(var j = 0, ci; ci = cs[j]; j++){
4809                     result[++ri] = ci;
4810                 }
4811             }
4812         }else if(mode == "/" || mode == ">"){
4813             var utag = tagName.toUpperCase();
4814             for(var i = 0, ni, cn; ni = ns[i]; i++){
4815                 cn = ni.children || ni.childNodes;
4816                 for(var j = 0, cj; cj = cn[j]; j++){
4817                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4818                         result[++ri] = cj;
4819                     }
4820                 }
4821             }
4822         }else if(mode == "+"){
4823             var utag = tagName.toUpperCase();
4824             for(var i = 0, n; n = ns[i]; i++){
4825                 while((n = n.nextSibling) && n.nodeType != 1);
4826                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4827                     result[++ri] = n;
4828                 }
4829             }
4830         }else if(mode == "~"){
4831             for(var i = 0, n; n = ns[i]; i++){
4832                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4833                 if(n){
4834                     result[++ri] = n;
4835                 }
4836             }
4837         }
4838         return result;
4839     };
4840
4841     function concat(a, b){
4842         if(b.slice){
4843             return a.concat(b);
4844         }
4845         for(var i = 0, l = b.length; i < l; i++){
4846             a[a.length] = b[i];
4847         }
4848         return a;
4849     }
4850
4851     function byTag(cs, tagName){
4852         if(cs.tagName || cs == document){
4853             cs = [cs];
4854         }
4855         if(!tagName){
4856             return cs;
4857         }
4858         var r = [], ri = -1;
4859         tagName = tagName.toLowerCase();
4860         for(var i = 0, ci; ci = cs[i]; i++){
4861             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4862                 r[++ri] = ci;
4863             }
4864         }
4865         return r;
4866     };
4867
4868     function byId(cs, attr, id){
4869         if(cs.tagName || cs == document){
4870             cs = [cs];
4871         }
4872         if(!id){
4873             return cs;
4874         }
4875         var r = [], ri = -1;
4876         for(var i = 0,ci; ci = cs[i]; i++){
4877             if(ci && ci.id == id){
4878                 r[++ri] = ci;
4879                 return r;
4880             }
4881         }
4882         return r;
4883     };
4884
4885     function byAttribute(cs, attr, value, op, custom){
4886         var r = [], ri = -1, st = custom=="{";
4887         var f = Roo.DomQuery.operators[op];
4888         for(var i = 0, ci; ci = cs[i]; i++){
4889             var a;
4890             if(st){
4891                 a = Roo.DomQuery.getStyle(ci, attr);
4892             }
4893             else if(attr == "class" || attr == "className"){
4894                 a = ci.className;
4895             }else if(attr == "for"){
4896                 a = ci.htmlFor;
4897             }else if(attr == "href"){
4898                 a = ci.getAttribute("href", 2);
4899             }else{
4900                 a = ci.getAttribute(attr);
4901             }
4902             if((f && f(a, value)) || (!f && a)){
4903                 r[++ri] = ci;
4904             }
4905         }
4906         return r;
4907     };
4908
4909     function byPseudo(cs, name, value){
4910         return Roo.DomQuery.pseudos[name](cs, value);
4911     };
4912
4913     // This is for IE MSXML which does not support expandos.
4914     // IE runs the same speed using setAttribute, however FF slows way down
4915     // and Safari completely fails so they need to continue to use expandos.
4916     var isIE = window.ActiveXObject ? true : false;
4917
4918     // this eval is stop the compressor from
4919     // renaming the variable to something shorter
4920     
4921     /** eval:var:batch */
4922     var batch = 30803; 
4923
4924     var key = 30803;
4925
4926     function nodupIEXml(cs){
4927         var d = ++key;
4928         cs[0].setAttribute("_nodup", d);
4929         var r = [cs[0]];
4930         for(var i = 1, len = cs.length; i < len; i++){
4931             var c = cs[i];
4932             if(!c.getAttribute("_nodup") != d){
4933                 c.setAttribute("_nodup", d);
4934                 r[r.length] = c;
4935             }
4936         }
4937         for(var i = 0, len = cs.length; i < len; i++){
4938             cs[i].removeAttribute("_nodup");
4939         }
4940         return r;
4941     }
4942
4943     function nodup(cs){
4944         if(!cs){
4945             return [];
4946         }
4947         var len = cs.length, c, i, r = cs, cj, ri = -1;
4948         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4949             return cs;
4950         }
4951         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4952             return nodupIEXml(cs);
4953         }
4954         var d = ++key;
4955         cs[0]._nodup = d;
4956         for(i = 1; c = cs[i]; i++){
4957             if(c._nodup != d){
4958                 c._nodup = d;
4959             }else{
4960                 r = [];
4961                 for(var j = 0; j < i; j++){
4962                     r[++ri] = cs[j];
4963                 }
4964                 for(j = i+1; cj = cs[j]; j++){
4965                     if(cj._nodup != d){
4966                         cj._nodup = d;
4967                         r[++ri] = cj;
4968                     }
4969                 }
4970                 return r;
4971             }
4972         }
4973         return r;
4974     }
4975
4976     function quickDiffIEXml(c1, c2){
4977         var d = ++key;
4978         for(var i = 0, len = c1.length; i < len; i++){
4979             c1[i].setAttribute("_qdiff", d);
4980         }
4981         var r = [];
4982         for(var i = 0, len = c2.length; i < len; i++){
4983             if(c2[i].getAttribute("_qdiff") != d){
4984                 r[r.length] = c2[i];
4985             }
4986         }
4987         for(var i = 0, len = c1.length; i < len; i++){
4988            c1[i].removeAttribute("_qdiff");
4989         }
4990         return r;
4991     }
4992
4993     function quickDiff(c1, c2){
4994         var len1 = c1.length;
4995         if(!len1){
4996             return c2;
4997         }
4998         if(isIE && c1[0].selectSingleNode){
4999             return quickDiffIEXml(c1, c2);
5000         }
5001         var d = ++key;
5002         for(var i = 0; i < len1; i++){
5003             c1[i]._qdiff = d;
5004         }
5005         var r = [];
5006         for(var i = 0, len = c2.length; i < len; i++){
5007             if(c2[i]._qdiff != d){
5008                 r[r.length] = c2[i];
5009             }
5010         }
5011         return r;
5012     }
5013
5014     function quickId(ns, mode, root, id){
5015         if(ns == root){
5016            var d = root.ownerDocument || root;
5017            return d.getElementById(id);
5018         }
5019         ns = getNodes(ns, mode, "*");
5020         return byId(ns, null, id);
5021     }
5022
5023     return {
5024         getStyle : function(el, name){
5025             return Roo.fly(el).getStyle(name);
5026         },
5027         /**
5028          * Compiles a selector/xpath query into a reusable function. The returned function
5029          * takes one parameter "root" (optional), which is the context node from where the query should start.
5030          * @param {String} selector The selector/xpath query
5031          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5032          * @return {Function}
5033          */
5034         compile : function(path, type){
5035             type = type || "select";
5036             
5037             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5038             var q = path, mode, lq;
5039             var tk = Roo.DomQuery.matchers;
5040             var tklen = tk.length;
5041             var mm;
5042
5043             // accept leading mode switch
5044             var lmode = q.match(modeRe);
5045             if(lmode && lmode[1]){
5046                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5047                 q = q.replace(lmode[1], "");
5048             }
5049             // strip leading slashes
5050             while(path.substr(0, 1)=="/"){
5051                 path = path.substr(1);
5052             }
5053
5054             while(q && lq != q){
5055                 lq = q;
5056                 var tm = q.match(tagTokenRe);
5057                 if(type == "select"){
5058                     if(tm){
5059                         if(tm[1] == "#"){
5060                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5061                         }else{
5062                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5063                         }
5064                         q = q.replace(tm[0], "");
5065                     }else if(q.substr(0, 1) != '@'){
5066                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5067                     }
5068                 }else{
5069                     if(tm){
5070                         if(tm[1] == "#"){
5071                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5072                         }else{
5073                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5074                         }
5075                         q = q.replace(tm[0], "");
5076                     }
5077                 }
5078                 while(!(mm = q.match(modeRe))){
5079                     var matched = false;
5080                     for(var j = 0; j < tklen; j++){
5081                         var t = tk[j];
5082                         var m = q.match(t.re);
5083                         if(m){
5084                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5085                                                     return m[i];
5086                                                 });
5087                             q = q.replace(m[0], "");
5088                             matched = true;
5089                             break;
5090                         }
5091                     }
5092                     // prevent infinite loop on bad selector
5093                     if(!matched){
5094                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5095                     }
5096                 }
5097                 if(mm[1]){
5098                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5099                     q = q.replace(mm[1], "");
5100                 }
5101             }
5102             fn[fn.length] = "return nodup(n);\n}";
5103             
5104              /** 
5105               * list of variables that need from compression as they are used by eval.
5106              *  eval:var:batch 
5107              *  eval:var:nodup
5108              *  eval:var:byTag
5109              *  eval:var:ById
5110              *  eval:var:getNodes
5111              *  eval:var:quickId
5112              *  eval:var:mode
5113              *  eval:var:root
5114              *  eval:var:n
5115              *  eval:var:byClassName
5116              *  eval:var:byPseudo
5117              *  eval:var:byAttribute
5118              *  eval:var:attrValue
5119              * 
5120              **/ 
5121             eval(fn.join(""));
5122             return f;
5123         },
5124
5125         /**
5126          * Selects a group of elements.
5127          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5128          * @param {Node} root (optional) The start of the query (defaults to document).
5129          * @return {Array}
5130          */
5131         select : function(path, root, type){
5132             if(!root || root == document){
5133                 root = document;
5134             }
5135             if(typeof root == "string"){
5136                 root = document.getElementById(root);
5137             }
5138             var paths = path.split(",");
5139             var results = [];
5140             for(var i = 0, len = paths.length; i < len; i++){
5141                 var p = paths[i].replace(trimRe, "");
5142                 if(!cache[p]){
5143                     cache[p] = Roo.DomQuery.compile(p);
5144                     if(!cache[p]){
5145                         throw p + " is not a valid selector";
5146                     }
5147                 }
5148                 var result = cache[p](root);
5149                 if(result && result != document){
5150                     results = results.concat(result);
5151                 }
5152             }
5153             if(paths.length > 1){
5154                 return nodup(results);
5155             }
5156             return results;
5157         },
5158
5159         /**
5160          * Selects a single element.
5161          * @param {String} selector The selector/xpath query
5162          * @param {Node} root (optional) The start of the query (defaults to document).
5163          * @return {Element}
5164          */
5165         selectNode : function(path, root){
5166             return Roo.DomQuery.select(path, root)[0];
5167         },
5168
5169         /**
5170          * Selects the value of a node, optionally replacing null with the defaultValue.
5171          * @param {String} selector The selector/xpath query
5172          * @param {Node} root (optional) The start of the query (defaults to document).
5173          * @param {String} defaultValue
5174          */
5175         selectValue : function(path, root, defaultValue){
5176             path = path.replace(trimRe, "");
5177             if(!valueCache[path]){
5178                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5179             }
5180             var n = valueCache[path](root);
5181             n = n[0] ? n[0] : n;
5182             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5183             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5184         },
5185
5186         /**
5187          * Selects the value of a node, parsing integers and floats.
5188          * @param {String} selector The selector/xpath query
5189          * @param {Node} root (optional) The start of the query (defaults to document).
5190          * @param {Number} defaultValue
5191          * @return {Number}
5192          */
5193         selectNumber : function(path, root, defaultValue){
5194             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5195             return parseFloat(v);
5196         },
5197
5198         /**
5199          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5200          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5201          * @param {String} selector The simple selector to test
5202          * @return {Boolean}
5203          */
5204         is : function(el, ss){
5205             if(typeof el == "string"){
5206                 el = document.getElementById(el);
5207             }
5208             var isArray = (el instanceof Array);
5209             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5210             return isArray ? (result.length == el.length) : (result.length > 0);
5211         },
5212
5213         /**
5214          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5215          * @param {Array} el An array of elements to filter
5216          * @param {String} selector The simple selector to test
5217          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5218          * the selector instead of the ones that match
5219          * @return {Array}
5220          */
5221         filter : function(els, ss, nonMatches){
5222             ss = ss.replace(trimRe, "");
5223             if(!simpleCache[ss]){
5224                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5225             }
5226             var result = simpleCache[ss](els);
5227             return nonMatches ? quickDiff(result, els) : result;
5228         },
5229
5230         /**
5231          * Collection of matching regular expressions and code snippets.
5232          */
5233         matchers : [{
5234                 re: /^\.([\w-]+)/,
5235                 select: 'n = byClassName(n, null, " {1} ");'
5236             }, {
5237                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5238                 select: 'n = byPseudo(n, "{1}", "{2}");'
5239             },{
5240                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5241                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5242             }, {
5243                 re: /^#([\w-]+)/,
5244                 select: 'n = byId(n, null, "{1}");'
5245             },{
5246                 re: /^@([\w-]+)/,
5247                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5248             }
5249         ],
5250
5251         /**
5252          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5253          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5254          */
5255         operators : {
5256             "=" : function(a, v){
5257                 return a == v;
5258             },
5259             "!=" : function(a, v){
5260                 return a != v;
5261             },
5262             "^=" : function(a, v){
5263                 return a && a.substr(0, v.length) == v;
5264             },
5265             "$=" : function(a, v){
5266                 return a && a.substr(a.length-v.length) == v;
5267             },
5268             "*=" : function(a, v){
5269                 return a && a.indexOf(v) !== -1;
5270             },
5271             "%=" : function(a, v){
5272                 return (a % v) == 0;
5273             },
5274             "|=" : function(a, v){
5275                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5276             },
5277             "~=" : function(a, v){
5278                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5279             }
5280         },
5281
5282         /**
5283          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5284          * and the argument (if any) supplied in the selector.
5285          */
5286         pseudos : {
5287             "first-child" : function(c){
5288                 var r = [], ri = -1, n;
5289                 for(var i = 0, ci; ci = n = c[i]; i++){
5290                     while((n = n.previousSibling) && n.nodeType != 1);
5291                     if(!n){
5292                         r[++ri] = ci;
5293                     }
5294                 }
5295                 return r;
5296             },
5297
5298             "last-child" : function(c){
5299                 var r = [], ri = -1, n;
5300                 for(var i = 0, ci; ci = n = c[i]; i++){
5301                     while((n = n.nextSibling) && n.nodeType != 1);
5302                     if(!n){
5303                         r[++ri] = ci;
5304                     }
5305                 }
5306                 return r;
5307             },
5308
5309             "nth-child" : function(c, a) {
5310                 var r = [], ri = -1;
5311                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5312                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5313                 for(var i = 0, n; n = c[i]; i++){
5314                     var pn = n.parentNode;
5315                     if (batch != pn._batch) {
5316                         var j = 0;
5317                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5318                             if(cn.nodeType == 1){
5319                                cn.nodeIndex = ++j;
5320                             }
5321                         }
5322                         pn._batch = batch;
5323                     }
5324                     if (f == 1) {
5325                         if (l == 0 || n.nodeIndex == l){
5326                             r[++ri] = n;
5327                         }
5328                     } else if ((n.nodeIndex + l) % f == 0){
5329                         r[++ri] = n;
5330                     }
5331                 }
5332
5333                 return r;
5334             },
5335
5336             "only-child" : function(c){
5337                 var r = [], ri = -1;;
5338                 for(var i = 0, ci; ci = c[i]; i++){
5339                     if(!prev(ci) && !next(ci)){
5340                         r[++ri] = ci;
5341                     }
5342                 }
5343                 return r;
5344             },
5345
5346             "empty" : function(c){
5347                 var r = [], ri = -1;
5348                 for(var i = 0, ci; ci = c[i]; i++){
5349                     var cns = ci.childNodes, j = 0, cn, empty = true;
5350                     while(cn = cns[j]){
5351                         ++j;
5352                         if(cn.nodeType == 1 || cn.nodeType == 3){
5353                             empty = false;
5354                             break;
5355                         }
5356                     }
5357                     if(empty){
5358                         r[++ri] = ci;
5359                     }
5360                 }
5361                 return r;
5362             },
5363
5364             "contains" : function(c, v){
5365                 var r = [], ri = -1;
5366                 for(var i = 0, ci; ci = c[i]; i++){
5367                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5368                         r[++ri] = ci;
5369                     }
5370                 }
5371                 return r;
5372             },
5373
5374             "nodeValue" : function(c, v){
5375                 var r = [], ri = -1;
5376                 for(var i = 0, ci; ci = c[i]; i++){
5377                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5378                         r[++ri] = ci;
5379                     }
5380                 }
5381                 return r;
5382             },
5383
5384             "checked" : function(c){
5385                 var r = [], ri = -1;
5386                 for(var i = 0, ci; ci = c[i]; i++){
5387                     if(ci.checked == true){
5388                         r[++ri] = ci;
5389                     }
5390                 }
5391                 return r;
5392             },
5393
5394             "not" : function(c, ss){
5395                 return Roo.DomQuery.filter(c, ss, true);
5396             },
5397
5398             "odd" : function(c){
5399                 return this["nth-child"](c, "odd");
5400             },
5401
5402             "even" : function(c){
5403                 return this["nth-child"](c, "even");
5404             },
5405
5406             "nth" : function(c, a){
5407                 return c[a-1] || [];
5408             },
5409
5410             "first" : function(c){
5411                 return c[0] || [];
5412             },
5413
5414             "last" : function(c){
5415                 return c[c.length-1] || [];
5416             },
5417
5418             "has" : function(c, ss){
5419                 var s = Roo.DomQuery.select;
5420                 var r = [], ri = -1;
5421                 for(var i = 0, ci; ci = c[i]; i++){
5422                     if(s(ss, ci).length > 0){
5423                         r[++ri] = ci;
5424                     }
5425                 }
5426                 return r;
5427             },
5428
5429             "next" : function(c, ss){
5430                 var is = Roo.DomQuery.is;
5431                 var r = [], ri = -1;
5432                 for(var i = 0, ci; ci = c[i]; i++){
5433                     var n = next(ci);
5434                     if(n && is(n, ss)){
5435                         r[++ri] = ci;
5436                     }
5437                 }
5438                 return r;
5439             },
5440
5441             "prev" : function(c, ss){
5442                 var is = Roo.DomQuery.is;
5443                 var r = [], ri = -1;
5444                 for(var i = 0, ci; ci = c[i]; i++){
5445                     var n = prev(ci);
5446                     if(n && is(n, ss)){
5447                         r[++ri] = ci;
5448                     }
5449                 }
5450                 return r;
5451             }
5452         }
5453     };
5454 }();
5455
5456 /**
5457  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5458  * @param {String} path The selector/xpath query
5459  * @param {Node} root (optional) The start of the query (defaults to document).
5460  * @return {Array}
5461  * @member Roo
5462  * @method query
5463  */
5464 Roo.query = Roo.DomQuery.select;
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 /**
5477  * @class Roo.util.Observable
5478  * Base class that provides a common interface for publishing events. Subclasses are expected to
5479  * to have a property "events" with all the events defined.<br>
5480  * For example:
5481  * <pre><code>
5482  Employee = function(name){
5483     this.name = name;
5484     this.addEvents({
5485         "fired" : true,
5486         "quit" : true
5487     });
5488  }
5489  Roo.extend(Employee, Roo.util.Observable);
5490 </code></pre>
5491  * @param {Object} config properties to use (incuding events / listeners)
5492  */
5493
5494 Roo.util.Observable = function(cfg){
5495     
5496     cfg = cfg|| {};
5497     this.addEvents(cfg.events || {});
5498     if (cfg.events) {
5499         delete cfg.events; // make sure
5500     }
5501      
5502     Roo.apply(this, cfg);
5503     
5504     if(this.listeners){
5505         this.on(this.listeners);
5506         delete this.listeners;
5507     }
5508 };
5509 Roo.util.Observable.prototype = {
5510     /** 
5511  * @cfg {Object} listeners  list of events and functions to call for this object, 
5512  * For example :
5513  * <pre><code>
5514     listeners :  { 
5515        'click' : function(e) {
5516            ..... 
5517         } ,
5518         .... 
5519     } 
5520   </code></pre>
5521  */
5522     
5523     
5524     /**
5525      * Fires the specified event with the passed parameters (minus the event name).
5526      * @param {String} eventName
5527      * @param {Object...} args Variable number of parameters are passed to handlers
5528      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5529      */
5530     fireEvent : function(){
5531         var ce = this.events[arguments[0].toLowerCase()];
5532         if(typeof ce == "object"){
5533             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5534         }else{
5535             return true;
5536         }
5537     },
5538
5539     // private
5540     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5541
5542     /**
5543      * Appends an event handler to this component
5544      * @param {String}   eventName The type of event to listen for
5545      * @param {Function} handler The method the event invokes
5546      * @param {Object}   scope (optional) The scope in which to execute the handler
5547      * function. The handler function's "this" context.
5548      * @param {Object}   options (optional) An object containing handler configuration
5549      * properties. This may contain any of the following properties:<ul>
5550      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5551      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5552      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5553      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5554      * by the specified number of milliseconds. If the event fires again within that time, the original
5555      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5556      * </ul><br>
5557      * <p>
5558      * <b>Combining Options</b><br>
5559      * Using the options argument, it is possible to combine different types of listeners:<br>
5560      * <br>
5561      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5562                 <pre><code>
5563                 el.on('click', this.onClick, this, {
5564                         single: true,
5565                 delay: 100,
5566                 forumId: 4
5567                 });
5568                 </code></pre>
5569      * <p>
5570      * <b>Attaching multiple handlers in 1 call</b><br>
5571      * The method also allows for a single argument to be passed which is a config object containing properties
5572      * which specify multiple handlers.
5573      * <pre><code>
5574                 el.on({
5575                         'click': {
5576                         fn: this.onClick,
5577                         scope: this,
5578                         delay: 100
5579                 }, 
5580                 'mouseover': {
5581                         fn: this.onMouseOver,
5582                         scope: this
5583                 },
5584                 'mouseout': {
5585                         fn: this.onMouseOut,
5586                         scope: this
5587                 }
5588                 });
5589                 </code></pre>
5590      * <p>
5591      * Or a shorthand syntax which passes the same scope object to all handlers:
5592         <pre><code>
5593                 el.on({
5594                         'click': this.onClick,
5595                 'mouseover': this.onMouseOver,
5596                 'mouseout': this.onMouseOut,
5597                 scope: this
5598                 });
5599                 </code></pre>
5600      */
5601     addListener : function(eventName, fn, scope, o){
5602         if(typeof eventName == "object"){
5603             o = eventName;
5604             for(var e in o){
5605                 if(this.filterOptRe.test(e)){
5606                     continue;
5607                 }
5608                 if(typeof o[e] == "function"){
5609                     // shared options
5610                     this.addListener(e, o[e], o.scope,  o);
5611                 }else{
5612                     // individual options
5613                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5614                 }
5615             }
5616             return;
5617         }
5618         o = (!o || typeof o == "boolean") ? {} : o;
5619         eventName = eventName.toLowerCase();
5620         var ce = this.events[eventName] || true;
5621         if(typeof ce == "boolean"){
5622             ce = new Roo.util.Event(this, eventName);
5623             this.events[eventName] = ce;
5624         }
5625         ce.addListener(fn, scope, o);
5626     },
5627
5628     /**
5629      * Removes a listener
5630      * @param {String}   eventName     The type of event to listen for
5631      * @param {Function} handler        The handler to remove
5632      * @param {Object}   scope  (optional) The scope (this object) for the handler
5633      */
5634     removeListener : function(eventName, fn, scope){
5635         var ce = this.events[eventName.toLowerCase()];
5636         if(typeof ce == "object"){
5637             ce.removeListener(fn, scope);
5638         }
5639     },
5640
5641     /**
5642      * Removes all listeners for this object
5643      */
5644     purgeListeners : function(){
5645         for(var evt in this.events){
5646             if(typeof this.events[evt] == "object"){
5647                  this.events[evt].clearListeners();
5648             }
5649         }
5650     },
5651
5652     relayEvents : function(o, events){
5653         var createHandler = function(ename){
5654             return function(){
5655                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5656             };
5657         };
5658         for(var i = 0, len = events.length; i < len; i++){
5659             var ename = events[i];
5660             if(!this.events[ename]){ this.events[ename] = true; };
5661             o.on(ename, createHandler(ename), this);
5662         }
5663     },
5664
5665     /**
5666      * Used to define events on this Observable
5667      * @param {Object} object The object with the events defined
5668      */
5669     addEvents : function(o){
5670         if(!this.events){
5671             this.events = {};
5672         }
5673         Roo.applyIf(this.events, o);
5674     },
5675
5676     /**
5677      * Checks to see if this object has any listeners for a specified event
5678      * @param {String} eventName The name of the event to check for
5679      * @return {Boolean} True if the event is being listened for, else false
5680      */
5681     hasListener : function(eventName){
5682         var e = this.events[eventName];
5683         return typeof e == "object" && e.listeners.length > 0;
5684     }
5685 };
5686 /**
5687  * Appends an event handler to this element (shorthand for addListener)
5688  * @param {String}   eventName     The type of event to listen for
5689  * @param {Function} handler        The method the event invokes
5690  * @param {Object}   scope (optional) The scope in which to execute the handler
5691  * function. The handler function's "this" context.
5692  * @param {Object}   options  (optional)
5693  * @method
5694  */
5695 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5696 /**
5697  * Removes a listener (shorthand for removeListener)
5698  * @param {String}   eventName     The type of event to listen for
5699  * @param {Function} handler        The handler to remove
5700  * @param {Object}   scope  (optional) The scope (this object) for the handler
5701  * @method
5702  */
5703 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5704
5705 /**
5706  * Starts capture on the specified Observable. All events will be passed
5707  * to the supplied function with the event name + standard signature of the event
5708  * <b>before</b> the event is fired. If the supplied function returns false,
5709  * the event will not fire.
5710  * @param {Observable} o The Observable to capture
5711  * @param {Function} fn The function to call
5712  * @param {Object} scope (optional) The scope (this object) for the fn
5713  * @static
5714  */
5715 Roo.util.Observable.capture = function(o, fn, scope){
5716     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5717 };
5718
5719 /**
5720  * Removes <b>all</b> added captures from the Observable.
5721  * @param {Observable} o The Observable to release
5722  * @static
5723  */
5724 Roo.util.Observable.releaseCapture = function(o){
5725     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5726 };
5727
5728 (function(){
5729
5730     var createBuffered = function(h, o, scope){
5731         var task = new Roo.util.DelayedTask();
5732         return function(){
5733             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5734         };
5735     };
5736
5737     var createSingle = function(h, e, fn, scope){
5738         return function(){
5739             e.removeListener(fn, scope);
5740             return h.apply(scope, arguments);
5741         };
5742     };
5743
5744     var createDelayed = function(h, o, scope){
5745         return function(){
5746             var args = Array.prototype.slice.call(arguments, 0);
5747             setTimeout(function(){
5748                 h.apply(scope, args);
5749             }, o.delay || 10);
5750         };
5751     };
5752
5753     Roo.util.Event = function(obj, name){
5754         this.name = name;
5755         this.obj = obj;
5756         this.listeners = [];
5757     };
5758
5759     Roo.util.Event.prototype = {
5760         addListener : function(fn, scope, options){
5761             var o = options || {};
5762             scope = scope || this.obj;
5763             if(!this.isListening(fn, scope)){
5764                 var l = {fn: fn, scope: scope, options: o};
5765                 var h = fn;
5766                 if(o.delay){
5767                     h = createDelayed(h, o, scope);
5768                 }
5769                 if(o.single){
5770                     h = createSingle(h, this, fn, scope);
5771                 }
5772                 if(o.buffer){
5773                     h = createBuffered(h, o, scope);
5774                 }
5775                 l.fireFn = h;
5776                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5777                     this.listeners.push(l);
5778                 }else{
5779                     this.listeners = this.listeners.slice(0);
5780                     this.listeners.push(l);
5781                 }
5782             }
5783         },
5784
5785         findListener : function(fn, scope){
5786             scope = scope || this.obj;
5787             var ls = this.listeners;
5788             for(var i = 0, len = ls.length; i < len; i++){
5789                 var l = ls[i];
5790                 if(l.fn == fn && l.scope == scope){
5791                     return i;
5792                 }
5793             }
5794             return -1;
5795         },
5796
5797         isListening : function(fn, scope){
5798             return this.findListener(fn, scope) != -1;
5799         },
5800
5801         removeListener : function(fn, scope){
5802             var index;
5803             if((index = this.findListener(fn, scope)) != -1){
5804                 if(!this.firing){
5805                     this.listeners.splice(index, 1);
5806                 }else{
5807                     this.listeners = this.listeners.slice(0);
5808                     this.listeners.splice(index, 1);
5809                 }
5810                 return true;
5811             }
5812             return false;
5813         },
5814
5815         clearListeners : function(){
5816             this.listeners = [];
5817         },
5818
5819         fire : function(){
5820             var ls = this.listeners, scope, len = ls.length;
5821             if(len > 0){
5822                 this.firing = true;
5823                 var args = Array.prototype.slice.call(arguments, 0);
5824                 for(var i = 0; i < len; i++){
5825                     var l = ls[i];
5826                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5827                         this.firing = false;
5828                         return false;
5829                     }
5830                 }
5831                 this.firing = false;
5832             }
5833             return true;
5834         }
5835     };
5836 })();/*
5837  * Based on:
5838  * Ext JS Library 1.1.1
5839  * Copyright(c) 2006-2007, Ext JS, LLC.
5840  *
5841  * Originally Released Under LGPL - original licence link has changed is not relivant.
5842  *
5843  * Fork - LGPL
5844  * <script type="text/javascript">
5845  */
5846
5847 /**
5848  * @class Roo.EventManager
5849  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5850  * several useful events directly.
5851  * See {@link Roo.EventObject} for more details on normalized event objects.
5852  * @singleton
5853  */
5854 Roo.EventManager = function(){
5855     var docReadyEvent, docReadyProcId, docReadyState = false;
5856     var resizeEvent, resizeTask, textEvent, textSize;
5857     var E = Roo.lib.Event;
5858     var D = Roo.lib.Dom;
5859
5860
5861     var fireDocReady = function(){
5862         if(!docReadyState){
5863             docReadyState = true;
5864             Roo.isReady = true;
5865             if(docReadyProcId){
5866                 clearInterval(docReadyProcId);
5867             }
5868             if(Roo.isGecko || Roo.isOpera) {
5869                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5870             }
5871             if(Roo.isIE){
5872                 var defer = document.getElementById("ie-deferred-loader");
5873                 if(defer){
5874                     defer.onreadystatechange = null;
5875                     defer.parentNode.removeChild(defer);
5876                 }
5877             }
5878             if(docReadyEvent){
5879                 docReadyEvent.fire();
5880                 docReadyEvent.clearListeners();
5881             }
5882         }
5883     };
5884     
5885     var initDocReady = function(){
5886         docReadyEvent = new Roo.util.Event();
5887         if(Roo.isGecko || Roo.isOpera) {
5888             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5889         }else if(Roo.isIE){
5890             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5891             var defer = document.getElementById("ie-deferred-loader");
5892             defer.onreadystatechange = function(){
5893                 if(this.readyState == "complete"){
5894                     fireDocReady();
5895                 }
5896             };
5897         }else if(Roo.isSafari){ 
5898             docReadyProcId = setInterval(function(){
5899                 var rs = document.readyState;
5900                 if(rs == "complete") {
5901                     fireDocReady();     
5902                  }
5903             }, 10);
5904         }
5905         // no matter what, make sure it fires on load
5906         E.on(window, "load", fireDocReady);
5907     };
5908
5909     var createBuffered = function(h, o){
5910         var task = new Roo.util.DelayedTask(h);
5911         return function(e){
5912             // create new event object impl so new events don't wipe out properties
5913             e = new Roo.EventObjectImpl(e);
5914             task.delay(o.buffer, h, null, [e]);
5915         };
5916     };
5917
5918     var createSingle = function(h, el, ename, fn){
5919         return function(e){
5920             Roo.EventManager.removeListener(el, ename, fn);
5921             h(e);
5922         };
5923     };
5924
5925     var createDelayed = function(h, o){
5926         return function(e){
5927             // create new event object impl so new events don't wipe out properties
5928             e = new Roo.EventObjectImpl(e);
5929             setTimeout(function(){
5930                 h(e);
5931             }, o.delay || 10);
5932         };
5933     };
5934
5935     var listen = function(element, ename, opt, fn, scope){
5936         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5937         fn = fn || o.fn; scope = scope || o.scope;
5938         var el = Roo.getDom(element);
5939         if(!el){
5940             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5941         }
5942         var h = function(e){
5943             e = Roo.EventObject.setEvent(e);
5944             var t;
5945             if(o.delegate){
5946                 t = e.getTarget(o.delegate, el);
5947                 if(!t){
5948                     return;
5949                 }
5950             }else{
5951                 t = e.target;
5952             }
5953             if(o.stopEvent === true){
5954                 e.stopEvent();
5955             }
5956             if(o.preventDefault === true){
5957                e.preventDefault();
5958             }
5959             if(o.stopPropagation === true){
5960                 e.stopPropagation();
5961             }
5962
5963             if(o.normalized === false){
5964                 e = e.browserEvent;
5965             }
5966
5967             fn.call(scope || el, e, t, o);
5968         };
5969         if(o.delay){
5970             h = createDelayed(h, o);
5971         }
5972         if(o.single){
5973             h = createSingle(h, el, ename, fn);
5974         }
5975         if(o.buffer){
5976             h = createBuffered(h, o);
5977         }
5978         fn._handlers = fn._handlers || [];
5979         fn._handlers.push([Roo.id(el), ename, h]);
5980
5981         E.on(el, ename, h);
5982         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5983             el.addEventListener("DOMMouseScroll", h, false);
5984             E.on(window, 'unload', function(){
5985                 el.removeEventListener("DOMMouseScroll", h, false);
5986             });
5987         }
5988         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5989             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5990         }
5991         return h;
5992     };
5993
5994     var stopListening = function(el, ename, fn){
5995         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5996         if(hds){
5997             for(var i = 0, len = hds.length; i < len; i++){
5998                 var h = hds[i];
5999                 if(h[0] == id && h[1] == ename){
6000                     hd = h[2];
6001                     hds.splice(i, 1);
6002                     break;
6003                 }
6004             }
6005         }
6006         E.un(el, ename, hd);
6007         el = Roo.getDom(el);
6008         if(ename == "mousewheel" && el.addEventListener){
6009             el.removeEventListener("DOMMouseScroll", hd, false);
6010         }
6011         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6012             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6013         }
6014     };
6015
6016     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6017     
6018     var pub = {
6019         
6020         
6021         /** 
6022          * Fix for doc tools
6023          * @scope Roo.EventManager
6024          */
6025         
6026         
6027         /** 
6028          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6029          * object with a Roo.EventObject
6030          * @param {Function} fn        The method the event invokes
6031          * @param {Object}   scope    An object that becomes the scope of the handler
6032          * @param {boolean}  override If true, the obj passed in becomes
6033          *                             the execution scope of the listener
6034          * @return {Function} The wrapped function
6035          * @deprecated
6036          */
6037         wrap : function(fn, scope, override){
6038             return function(e){
6039                 Roo.EventObject.setEvent(e);
6040                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6041             };
6042         },
6043         
6044         /**
6045      * Appends an event handler to an element (shorthand for addListener)
6046      * @param {String/HTMLElement}   element        The html element or id to assign the
6047      * @param {String}   eventName The type of event to listen for
6048      * @param {Function} handler The method the event invokes
6049      * @param {Object}   scope (optional) The scope in which to execute the handler
6050      * function. The handler function's "this" context.
6051      * @param {Object}   options (optional) An object containing handler configuration
6052      * properties. This may contain any of the following properties:<ul>
6053      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6054      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6055      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6056      * <li>preventDefault {Boolean} True to prevent the default action</li>
6057      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6058      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6059      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6060      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6061      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6062      * by the specified number of milliseconds. If the event fires again within that time, the original
6063      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6064      * </ul><br>
6065      * <p>
6066      * <b>Combining Options</b><br>
6067      * Using the options argument, it is possible to combine different types of listeners:<br>
6068      * <br>
6069      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6070      * Code:<pre><code>
6071 el.on('click', this.onClick, this, {
6072     single: true,
6073     delay: 100,
6074     stopEvent : true,
6075     forumId: 4
6076 });</code></pre>
6077      * <p>
6078      * <b>Attaching multiple handlers in 1 call</b><br>
6079       * The method also allows for a single argument to be passed which is a config object containing properties
6080      * which specify multiple handlers.
6081      * <p>
6082      * Code:<pre><code>
6083 el.on({
6084     'click' : {
6085         fn: this.onClick
6086         scope: this,
6087         delay: 100
6088     },
6089     'mouseover' : {
6090         fn: this.onMouseOver
6091         scope: this
6092     },
6093     'mouseout' : {
6094         fn: this.onMouseOut
6095         scope: this
6096     }
6097 });</code></pre>
6098      * <p>
6099      * Or a shorthand syntax:<br>
6100      * Code:<pre><code>
6101 el.on({
6102     'click' : this.onClick,
6103     'mouseover' : this.onMouseOver,
6104     'mouseout' : this.onMouseOut
6105     scope: this
6106 });</code></pre>
6107      */
6108         addListener : function(element, eventName, fn, scope, options){
6109             if(typeof eventName == "object"){
6110                 var o = eventName;
6111                 for(var e in o){
6112                     if(propRe.test(e)){
6113                         continue;
6114                     }
6115                     if(typeof o[e] == "function"){
6116                         // shared options
6117                         listen(element, e, o, o[e], o.scope);
6118                     }else{
6119                         // individual options
6120                         listen(element, e, o[e]);
6121                     }
6122                 }
6123                 return;
6124             }
6125             return listen(element, eventName, options, fn, scope);
6126         },
6127         
6128         /**
6129          * Removes an event handler
6130          *
6131          * @param {String/HTMLElement}   element        The id or html element to remove the 
6132          *                             event from
6133          * @param {String}   eventName     The type of event
6134          * @param {Function} fn
6135          * @return {Boolean} True if a listener was actually removed
6136          */
6137         removeListener : function(element, eventName, fn){
6138             return stopListening(element, eventName, fn);
6139         },
6140         
6141         /**
6142          * Fires when the document is ready (before onload and before images are loaded). Can be 
6143          * accessed shorthanded Roo.onReady().
6144          * @param {Function} fn        The method the event invokes
6145          * @param {Object}   scope    An  object that becomes the scope of the handler
6146          * @param {boolean}  options
6147          */
6148         onDocumentReady : function(fn, scope, options){
6149             if(docReadyState){ // if it already fired
6150                 docReadyEvent.addListener(fn, scope, options);
6151                 docReadyEvent.fire();
6152                 docReadyEvent.clearListeners();
6153                 return;
6154             }
6155             if(!docReadyEvent){
6156                 initDocReady();
6157             }
6158             docReadyEvent.addListener(fn, scope, options);
6159         },
6160         
6161         /**
6162          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6163          * @param {Function} fn        The method the event invokes
6164          * @param {Object}   scope    An object that becomes the scope of the handler
6165          * @param {boolean}  options
6166          */
6167         onWindowResize : function(fn, scope, options){
6168             if(!resizeEvent){
6169                 resizeEvent = new Roo.util.Event();
6170                 resizeTask = new Roo.util.DelayedTask(function(){
6171                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6172                 });
6173                 E.on(window, "resize", function(){
6174                     if(Roo.isIE){
6175                         resizeTask.delay(50);
6176                     }else{
6177                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6178                     }
6179                 });
6180             }
6181             resizeEvent.addListener(fn, scope, options);
6182         },
6183
6184         /**
6185          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6186          * @param {Function} fn        The method the event invokes
6187          * @param {Object}   scope    An object that becomes the scope of the handler
6188          * @param {boolean}  options
6189          */
6190         onTextResize : function(fn, scope, options){
6191             if(!textEvent){
6192                 textEvent = new Roo.util.Event();
6193                 var textEl = new Roo.Element(document.createElement('div'));
6194                 textEl.dom.className = 'x-text-resize';
6195                 textEl.dom.innerHTML = 'X';
6196                 textEl.appendTo(document.body);
6197                 textSize = textEl.dom.offsetHeight;
6198                 setInterval(function(){
6199                     if(textEl.dom.offsetHeight != textSize){
6200                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6201                     }
6202                 }, this.textResizeInterval);
6203             }
6204             textEvent.addListener(fn, scope, options);
6205         },
6206
6207         /**
6208          * Removes the passed window resize listener.
6209          * @param {Function} fn        The method the event invokes
6210          * @param {Object}   scope    The scope of handler
6211          */
6212         removeResizeListener : function(fn, scope){
6213             if(resizeEvent){
6214                 resizeEvent.removeListener(fn, scope);
6215             }
6216         },
6217
6218         // private
6219         fireResize : function(){
6220             if(resizeEvent){
6221                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6222             }   
6223         },
6224         /**
6225          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6226          */
6227         ieDeferSrc : false,
6228         /**
6229          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6230          */
6231         textResizeInterval : 50
6232     };
6233     
6234     /**
6235      * Fix for doc tools
6236      * @scopeAlias pub=Roo.EventManager
6237      */
6238     
6239      /**
6240      * Appends an event handler to an element (shorthand for addListener)
6241      * @param {String/HTMLElement}   element        The html element or id to assign the
6242      * @param {String}   eventName The type of event to listen for
6243      * @param {Function} handler The method the event invokes
6244      * @param {Object}   scope (optional) The scope in which to execute the handler
6245      * function. The handler function's "this" context.
6246      * @param {Object}   options (optional) An object containing handler configuration
6247      * properties. This may contain any of the following properties:<ul>
6248      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6249      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6250      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6251      * <li>preventDefault {Boolean} True to prevent the default action</li>
6252      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6253      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6254      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6255      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6256      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6257      * by the specified number of milliseconds. If the event fires again within that time, the original
6258      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6259      * </ul><br>
6260      * <p>
6261      * <b>Combining Options</b><br>
6262      * Using the options argument, it is possible to combine different types of listeners:<br>
6263      * <br>
6264      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6265      * Code:<pre><code>
6266 el.on('click', this.onClick, this, {
6267     single: true,
6268     delay: 100,
6269     stopEvent : true,
6270     forumId: 4
6271 });</code></pre>
6272      * <p>
6273      * <b>Attaching multiple handlers in 1 call</b><br>
6274       * The method also allows for a single argument to be passed which is a config object containing properties
6275      * which specify multiple handlers.
6276      * <p>
6277      * Code:<pre><code>
6278 el.on({
6279     'click' : {
6280         fn: this.onClick
6281         scope: this,
6282         delay: 100
6283     },
6284     'mouseover' : {
6285         fn: this.onMouseOver
6286         scope: this
6287     },
6288     'mouseout' : {
6289         fn: this.onMouseOut
6290         scope: this
6291     }
6292 });</code></pre>
6293      * <p>
6294      * Or a shorthand syntax:<br>
6295      * Code:<pre><code>
6296 el.on({
6297     'click' : this.onClick,
6298     'mouseover' : this.onMouseOver,
6299     'mouseout' : this.onMouseOut
6300     scope: this
6301 });</code></pre>
6302      */
6303     pub.on = pub.addListener;
6304     pub.un = pub.removeListener;
6305
6306     pub.stoppedMouseDownEvent = new Roo.util.Event();
6307     return pub;
6308 }();
6309 /**
6310   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6311   * @param {Function} fn        The method the event invokes
6312   * @param {Object}   scope    An  object that becomes the scope of the handler
6313   * @param {boolean}  override If true, the obj passed in becomes
6314   *                             the execution scope of the listener
6315   * @member Roo
6316   * @method onReady
6317  */
6318 Roo.onReady = Roo.EventManager.onDocumentReady;
6319
6320 Roo.onReady(function(){
6321     var bd = Roo.get(document.body);
6322     if(!bd){ return; }
6323
6324     var cls = [
6325             Roo.isIE ? "roo-ie"
6326             : Roo.isGecko ? "roo-gecko"
6327             : Roo.isOpera ? "roo-opera"
6328             : Roo.isSafari ? "roo-safari" : ""];
6329
6330     if(Roo.isMac){
6331         cls.push("roo-mac");
6332     }
6333     if(Roo.isLinux){
6334         cls.push("roo-linux");
6335     }
6336     if(Roo.isBorderBox){
6337         cls.push('roo-border-box');
6338     }
6339     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6340         var p = bd.dom.parentNode;
6341         if(p){
6342             p.className += ' roo-strict';
6343         }
6344     }
6345     bd.addClass(cls.join(' '));
6346 });
6347
6348 /**
6349  * @class Roo.EventObject
6350  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6351  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6352  * Example:
6353  * <pre><code>
6354  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6355     e.preventDefault();
6356     var target = e.getTarget();
6357     ...
6358  }
6359  var myDiv = Roo.get("myDiv");
6360  myDiv.on("click", handleClick);
6361  //or
6362  Roo.EventManager.on("myDiv", 'click', handleClick);
6363  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6364  </code></pre>
6365  * @singleton
6366  */
6367 Roo.EventObject = function(){
6368     
6369     var E = Roo.lib.Event;
6370     
6371     // safari keypress events for special keys return bad keycodes
6372     var safariKeys = {
6373         63234 : 37, // left
6374         63235 : 39, // right
6375         63232 : 38, // up
6376         63233 : 40, // down
6377         63276 : 33, // page up
6378         63277 : 34, // page down
6379         63272 : 46, // delete
6380         63273 : 36, // home
6381         63275 : 35  // end
6382     };
6383
6384     // normalize button clicks
6385     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6386                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6387
6388     Roo.EventObjectImpl = function(e){
6389         if(e){
6390             this.setEvent(e.browserEvent || e);
6391         }
6392     };
6393     Roo.EventObjectImpl.prototype = {
6394         /**
6395          * Used to fix doc tools.
6396          * @scope Roo.EventObject.prototype
6397          */
6398             
6399
6400         
6401         
6402         /** The normal browser event */
6403         browserEvent : null,
6404         /** The button pressed in a mouse event */
6405         button : -1,
6406         /** True if the shift key was down during the event */
6407         shiftKey : false,
6408         /** True if the control key was down during the event */
6409         ctrlKey : false,
6410         /** True if the alt key was down during the event */
6411         altKey : false,
6412
6413         /** Key constant 
6414         * @type Number */
6415         BACKSPACE : 8,
6416         /** Key constant 
6417         * @type Number */
6418         TAB : 9,
6419         /** Key constant 
6420         * @type Number */
6421         RETURN : 13,
6422         /** Key constant 
6423         * @type Number */
6424         ENTER : 13,
6425         /** Key constant 
6426         * @type Number */
6427         SHIFT : 16,
6428         /** Key constant 
6429         * @type Number */
6430         CONTROL : 17,
6431         /** Key constant 
6432         * @type Number */
6433         ESC : 27,
6434         /** Key constant 
6435         * @type Number */
6436         SPACE : 32,
6437         /** Key constant 
6438         * @type Number */
6439         PAGEUP : 33,
6440         /** Key constant 
6441         * @type Number */
6442         PAGEDOWN : 34,
6443         /** Key constant 
6444         * @type Number */
6445         END : 35,
6446         /** Key constant 
6447         * @type Number */
6448         HOME : 36,
6449         /** Key constant 
6450         * @type Number */
6451         LEFT : 37,
6452         /** Key constant 
6453         * @type Number */
6454         UP : 38,
6455         /** Key constant 
6456         * @type Number */
6457         RIGHT : 39,
6458         /** Key constant 
6459         * @type Number */
6460         DOWN : 40,
6461         /** Key constant 
6462         * @type Number */
6463         DELETE : 46,
6464         /** Key constant 
6465         * @type Number */
6466         F5 : 116,
6467
6468            /** @private */
6469         setEvent : function(e){
6470             if(e == this || (e && e.browserEvent)){ // already wrapped
6471                 return e;
6472             }
6473             this.browserEvent = e;
6474             if(e){
6475                 // normalize buttons
6476                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6477                 if(e.type == 'click' && this.button == -1){
6478                     this.button = 0;
6479                 }
6480                 this.type = e.type;
6481                 this.shiftKey = e.shiftKey;
6482                 // mac metaKey behaves like ctrlKey
6483                 this.ctrlKey = e.ctrlKey || e.metaKey;
6484                 this.altKey = e.altKey;
6485                 // in getKey these will be normalized for the mac
6486                 this.keyCode = e.keyCode;
6487                 // keyup warnings on firefox.
6488                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6489                 // cache the target for the delayed and or buffered events
6490                 this.target = E.getTarget(e);
6491                 // same for XY
6492                 this.xy = E.getXY(e);
6493             }else{
6494                 this.button = -1;
6495                 this.shiftKey = false;
6496                 this.ctrlKey = false;
6497                 this.altKey = false;
6498                 this.keyCode = 0;
6499                 this.charCode =0;
6500                 this.target = null;
6501                 this.xy = [0, 0];
6502             }
6503             return this;
6504         },
6505
6506         /**
6507          * Stop the event (preventDefault and stopPropagation)
6508          */
6509         stopEvent : function(){
6510             if(this.browserEvent){
6511                 if(this.browserEvent.type == 'mousedown'){
6512                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6513                 }
6514                 E.stopEvent(this.browserEvent);
6515             }
6516         },
6517
6518         /**
6519          * Prevents the browsers default handling of the event.
6520          */
6521         preventDefault : function(){
6522             if(this.browserEvent){
6523                 E.preventDefault(this.browserEvent);
6524             }
6525         },
6526
6527         /** @private */
6528         isNavKeyPress : function(){
6529             var k = this.keyCode;
6530             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6531             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6532         },
6533
6534         isSpecialKey : function(){
6535             var k = this.keyCode;
6536             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6537             (k == 16) || (k == 17) ||
6538             (k >= 18 && k <= 20) ||
6539             (k >= 33 && k <= 35) ||
6540             (k >= 36 && k <= 39) ||
6541             (k >= 44 && k <= 45);
6542         },
6543         /**
6544          * Cancels bubbling of the event.
6545          */
6546         stopPropagation : function(){
6547             if(this.browserEvent){
6548                 if(this.type == 'mousedown'){
6549                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6550                 }
6551                 E.stopPropagation(this.browserEvent);
6552             }
6553         },
6554
6555         /**
6556          * Gets the key code for the event.
6557          * @return {Number}
6558          */
6559         getCharCode : function(){
6560             return this.charCode || this.keyCode;
6561         },
6562
6563         /**
6564          * Returns a normalized keyCode for the event.
6565          * @return {Number} The key code
6566          */
6567         getKey : function(){
6568             var k = this.keyCode || this.charCode;
6569             return Roo.isSafari ? (safariKeys[k] || k) : k;
6570         },
6571
6572         /**
6573          * Gets the x coordinate of the event.
6574          * @return {Number}
6575          */
6576         getPageX : function(){
6577             return this.xy[0];
6578         },
6579
6580         /**
6581          * Gets the y coordinate of the event.
6582          * @return {Number}
6583          */
6584         getPageY : function(){
6585             return this.xy[1];
6586         },
6587
6588         /**
6589          * Gets the time of the event.
6590          * @return {Number}
6591          */
6592         getTime : function(){
6593             if(this.browserEvent){
6594                 return E.getTime(this.browserEvent);
6595             }
6596             return null;
6597         },
6598
6599         /**
6600          * Gets the page coordinates of the event.
6601          * @return {Array} The xy values like [x, y]
6602          */
6603         getXY : function(){
6604             return this.xy;
6605         },
6606
6607         /**
6608          * Gets the target for the event.
6609          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6610          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6611                 search as a number or element (defaults to 10 || document.body)
6612          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6613          * @return {HTMLelement}
6614          */
6615         getTarget : function(selector, maxDepth, returnEl){
6616             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6617         },
6618         /**
6619          * Gets the related target.
6620          * @return {HTMLElement}
6621          */
6622         getRelatedTarget : function(){
6623             if(this.browserEvent){
6624                 return E.getRelatedTarget(this.browserEvent);
6625             }
6626             return null;
6627         },
6628
6629         /**
6630          * Normalizes mouse wheel delta across browsers
6631          * @return {Number} The delta
6632          */
6633         getWheelDelta : function(){
6634             var e = this.browserEvent;
6635             var delta = 0;
6636             if(e.wheelDelta){ /* IE/Opera. */
6637                 delta = e.wheelDelta/120;
6638             }else if(e.detail){ /* Mozilla case. */
6639                 delta = -e.detail/3;
6640             }
6641             return delta;
6642         },
6643
6644         /**
6645          * Returns true if the control, meta, shift or alt key was pressed during this event.
6646          * @return {Boolean}
6647          */
6648         hasModifier : function(){
6649             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6650         },
6651
6652         /**
6653          * Returns true if the target of this event equals el or is a child of el
6654          * @param {String/HTMLElement/Element} el
6655          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6656          * @return {Boolean}
6657          */
6658         within : function(el, related){
6659             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6660             return t && Roo.fly(el).contains(t);
6661         },
6662
6663         getPoint : function(){
6664             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6665         }
6666     };
6667
6668     return new Roo.EventObjectImpl();
6669 }();
6670             
6671     /*
6672  * Based on:
6673  * Ext JS Library 1.1.1
6674  * Copyright(c) 2006-2007, Ext JS, LLC.
6675  *
6676  * Originally Released Under LGPL - original licence link has changed is not relivant.
6677  *
6678  * Fork - LGPL
6679  * <script type="text/javascript">
6680  */
6681
6682  
6683 // was in Composite Element!??!?!
6684  
6685 (function(){
6686     var D = Roo.lib.Dom;
6687     var E = Roo.lib.Event;
6688     var A = Roo.lib.Anim;
6689
6690     // local style camelizing for speed
6691     var propCache = {};
6692     var camelRe = /(-[a-z])/gi;
6693     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6694     var view = document.defaultView;
6695
6696 /**
6697  * @class Roo.Element
6698  * Represents an Element in the DOM.<br><br>
6699  * Usage:<br>
6700 <pre><code>
6701 var el = Roo.get("my-div");
6702
6703 // or with getEl
6704 var el = getEl("my-div");
6705
6706 // or with a DOM element
6707 var el = Roo.get(myDivElement);
6708 </code></pre>
6709  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6710  * each call instead of constructing a new one.<br><br>
6711  * <b>Animations</b><br />
6712  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6713  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6714 <pre>
6715 Option    Default   Description
6716 --------- --------  ---------------------------------------------
6717 duration  .35       The duration of the animation in seconds
6718 easing    easeOut   The YUI easing method
6719 callback  none      A function to execute when the anim completes
6720 scope     this      The scope (this) of the callback function
6721 </pre>
6722 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6723 * manipulate the animation. Here's an example:
6724 <pre><code>
6725 var el = Roo.get("my-div");
6726
6727 // no animation
6728 el.setWidth(100);
6729
6730 // default animation
6731 el.setWidth(100, true);
6732
6733 // animation with some options set
6734 el.setWidth(100, {
6735     duration: 1,
6736     callback: this.foo,
6737     scope: this
6738 });
6739
6740 // using the "anim" property to get the Anim object
6741 var opt = {
6742     duration: 1,
6743     callback: this.foo,
6744     scope: this
6745 };
6746 el.setWidth(100, opt);
6747 ...
6748 if(opt.anim.isAnimated()){
6749     opt.anim.stop();
6750 }
6751 </code></pre>
6752 * <b> Composite (Collections of) Elements</b><br />
6753  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6754  * @constructor Create a new Element directly.
6755  * @param {String/HTMLElement} element
6756  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6757  */
6758     Roo.Element = function(element, forceNew){
6759         var dom = typeof element == "string" ?
6760                 document.getElementById(element) : element;
6761         if(!dom){ // invalid id/element
6762             return null;
6763         }
6764         var id = dom.id;
6765         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6766             return Roo.Element.cache[id];
6767         }
6768
6769         /**
6770          * The DOM element
6771          * @type HTMLElement
6772          */
6773         this.dom = dom;
6774
6775         /**
6776          * The DOM element ID
6777          * @type String
6778          */
6779         this.id = id || Roo.id(dom);
6780     };
6781
6782     var El = Roo.Element;
6783
6784     El.prototype = {
6785         /**
6786          * The element's default display mode  (defaults to "")
6787          * @type String
6788          */
6789         originalDisplay : "",
6790
6791         visibilityMode : 1,
6792         /**
6793          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6794          * @type String
6795          */
6796         defaultUnit : "px",
6797         /**
6798          * Sets the element's visibility mode. When setVisible() is called it
6799          * will use this to determine whether to set the visibility or the display property.
6800          * @param visMode Element.VISIBILITY or Element.DISPLAY
6801          * @return {Roo.Element} this
6802          */
6803         setVisibilityMode : function(visMode){
6804             this.visibilityMode = visMode;
6805             return this;
6806         },
6807         /**
6808          * Convenience method for setVisibilityMode(Element.DISPLAY)
6809          * @param {String} display (optional) What to set display to when visible
6810          * @return {Roo.Element} this
6811          */
6812         enableDisplayMode : function(display){
6813             this.setVisibilityMode(El.DISPLAY);
6814             if(typeof display != "undefined") this.originalDisplay = display;
6815             return this;
6816         },
6817
6818         /**
6819          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6820          * @param {String} selector The simple selector to test
6821          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6822                 search as a number or element (defaults to 10 || document.body)
6823          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6824          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6825          */
6826         findParent : function(simpleSelector, maxDepth, returnEl){
6827             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6828             maxDepth = maxDepth || 50;
6829             if(typeof maxDepth != "number"){
6830                 stopEl = Roo.getDom(maxDepth);
6831                 maxDepth = 10;
6832             }
6833             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6834                 if(dq.is(p, simpleSelector)){
6835                     return returnEl ? Roo.get(p) : p;
6836                 }
6837                 depth++;
6838                 p = p.parentNode;
6839             }
6840             return null;
6841         },
6842
6843
6844         /**
6845          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6846          * @param {String} selector The simple selector to test
6847          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6848                 search as a number or element (defaults to 10 || document.body)
6849          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6850          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6851          */
6852         findParentNode : function(simpleSelector, maxDepth, returnEl){
6853             var p = Roo.fly(this.dom.parentNode, '_internal');
6854             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6855         },
6856
6857         /**
6858          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6859          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6860          * @param {String} selector The simple selector to test
6861          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6862                 search as a number or element (defaults to 10 || document.body)
6863          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6864          */
6865         up : function(simpleSelector, maxDepth){
6866             return this.findParentNode(simpleSelector, maxDepth, true);
6867         },
6868
6869
6870
6871         /**
6872          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6873          * @param {String} selector The simple selector to test
6874          * @return {Boolean} True if this element matches the selector, else false
6875          */
6876         is : function(simpleSelector){
6877             return Roo.DomQuery.is(this.dom, simpleSelector);
6878         },
6879
6880         /**
6881          * Perform animation on this element.
6882          * @param {Object} args The YUI animation control args
6883          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6884          * @param {Function} onComplete (optional) Function to call when animation completes
6885          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6886          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6887          * @return {Roo.Element} this
6888          */
6889         animate : function(args, duration, onComplete, easing, animType){
6890             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6891             return this;
6892         },
6893
6894         /*
6895          * @private Internal animation call
6896          */
6897         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6898             animType = animType || 'run';
6899             opt = opt || {};
6900             var anim = Roo.lib.Anim[animType](
6901                 this.dom, args,
6902                 (opt.duration || defaultDur) || .35,
6903                 (opt.easing || defaultEase) || 'easeOut',
6904                 function(){
6905                     Roo.callback(cb, this);
6906                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6907                 },
6908                 this
6909             );
6910             opt.anim = anim;
6911             return anim;
6912         },
6913
6914         // private legacy anim prep
6915         preanim : function(a, i){
6916             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6917         },
6918
6919         /**
6920          * Removes worthless text nodes
6921          * @param {Boolean} forceReclean (optional) By default the element
6922          * keeps track if it has been cleaned already so
6923          * you can call this over and over. However, if you update the element and
6924          * need to force a reclean, you can pass true.
6925          */
6926         clean : function(forceReclean){
6927             if(this.isCleaned && forceReclean !== true){
6928                 return this;
6929             }
6930             var ns = /\S/;
6931             var d = this.dom, n = d.firstChild, ni = -1;
6932             while(n){
6933                 var nx = n.nextSibling;
6934                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6935                     d.removeChild(n);
6936                 }else{
6937                     n.nodeIndex = ++ni;
6938                 }
6939                 n = nx;
6940             }
6941             this.isCleaned = true;
6942             return this;
6943         },
6944
6945         // private
6946         calcOffsetsTo : function(el){
6947             el = Roo.get(el);
6948             var d = el.dom;
6949             var restorePos = false;
6950             if(el.getStyle('position') == 'static'){
6951                 el.position('relative');
6952                 restorePos = true;
6953             }
6954             var x = 0, y =0;
6955             var op = this.dom;
6956             while(op && op != d && op.tagName != 'HTML'){
6957                 x+= op.offsetLeft;
6958                 y+= op.offsetTop;
6959                 op = op.offsetParent;
6960             }
6961             if(restorePos){
6962                 el.position('static');
6963             }
6964             return [x, y];
6965         },
6966
6967         /**
6968          * Scrolls this element into view within the passed container.
6969          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6970          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6971          * @return {Roo.Element} this
6972          */
6973         scrollIntoView : function(container, hscroll){
6974             var c = Roo.getDom(container) || document.body;
6975             var el = this.dom;
6976
6977             var o = this.calcOffsetsTo(c),
6978                 l = o[0],
6979                 t = o[1],
6980                 b = t+el.offsetHeight,
6981                 r = l+el.offsetWidth;
6982
6983             var ch = c.clientHeight;
6984             var ct = parseInt(c.scrollTop, 10);
6985             var cl = parseInt(c.scrollLeft, 10);
6986             var cb = ct + ch;
6987             var cr = cl + c.clientWidth;
6988
6989             if(t < ct){
6990                 c.scrollTop = t;
6991             }else if(b > cb){
6992                 c.scrollTop = b-ch;
6993             }
6994
6995             if(hscroll !== false){
6996                 if(l < cl){
6997                     c.scrollLeft = l;
6998                 }else if(r > cr){
6999                     c.scrollLeft = r-c.clientWidth;
7000                 }
7001             }
7002             return this;
7003         },
7004
7005         // private
7006         scrollChildIntoView : function(child, hscroll){
7007             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7008         },
7009
7010         /**
7011          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7012          * the new height may not be available immediately.
7013          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7014          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7015          * @param {Function} onComplete (optional) Function to call when animation completes
7016          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7017          * @return {Roo.Element} this
7018          */
7019         autoHeight : function(animate, duration, onComplete, easing){
7020             var oldHeight = this.getHeight();
7021             this.clip();
7022             this.setHeight(1); // force clipping
7023             setTimeout(function(){
7024                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7025                 if(!animate){
7026                     this.setHeight(height);
7027                     this.unclip();
7028                     if(typeof onComplete == "function"){
7029                         onComplete();
7030                     }
7031                 }else{
7032                     this.setHeight(oldHeight); // restore original height
7033                     this.setHeight(height, animate, duration, function(){
7034                         this.unclip();
7035                         if(typeof onComplete == "function") onComplete();
7036                     }.createDelegate(this), easing);
7037                 }
7038             }.createDelegate(this), 0);
7039             return this;
7040         },
7041
7042         /**
7043          * Returns true if this element is an ancestor of the passed element
7044          * @param {HTMLElement/String} el The element to check
7045          * @return {Boolean} True if this element is an ancestor of el, else false
7046          */
7047         contains : function(el){
7048             if(!el){return false;}
7049             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7050         },
7051
7052         /**
7053          * Checks whether the element is currently visible using both visibility and display properties.
7054          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7055          * @return {Boolean} True if the element is currently visible, else false
7056          */
7057         isVisible : function(deep) {
7058             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7059             if(deep !== true || !vis){
7060                 return vis;
7061             }
7062             var p = this.dom.parentNode;
7063             while(p && p.tagName.toLowerCase() != "body"){
7064                 if(!Roo.fly(p, '_isVisible').isVisible()){
7065                     return false;
7066                 }
7067                 p = p.parentNode;
7068             }
7069             return true;
7070         },
7071
7072         /**
7073          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7074          * @param {String} selector The CSS selector
7075          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7076          * @return {CompositeElement/CompositeElementLite} The composite element
7077          */
7078         select : function(selector, unique){
7079             return El.select(selector, unique, this.dom);
7080         },
7081
7082         /**
7083          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7084          * @param {String} selector The CSS selector
7085          * @return {Array} An array of the matched nodes
7086          */
7087         query : function(selector, unique){
7088             return Roo.DomQuery.select(selector, this.dom);
7089         },
7090
7091         /**
7092          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7093          * @param {String} selector The CSS selector
7094          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7095          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7096          */
7097         child : function(selector, returnDom){
7098             var n = Roo.DomQuery.selectNode(selector, this.dom);
7099             return returnDom ? n : Roo.get(n);
7100         },
7101
7102         /**
7103          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7104          * @param {String} selector The CSS selector
7105          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7106          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7107          */
7108         down : function(selector, returnDom){
7109             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7110             return returnDom ? n : Roo.get(n);
7111         },
7112
7113         /**
7114          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7115          * @param {String} group The group the DD object is member of
7116          * @param {Object} config The DD config object
7117          * @param {Object} overrides An object containing methods to override/implement on the DD object
7118          * @return {Roo.dd.DD} The DD object
7119          */
7120         initDD : function(group, config, overrides){
7121             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7122             return Roo.apply(dd, overrides);
7123         },
7124
7125         /**
7126          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7127          * @param {String} group The group the DDProxy object is member of
7128          * @param {Object} config The DDProxy config object
7129          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7130          * @return {Roo.dd.DDProxy} The DDProxy object
7131          */
7132         initDDProxy : function(group, config, overrides){
7133             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7134             return Roo.apply(dd, overrides);
7135         },
7136
7137         /**
7138          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7139          * @param {String} group The group the DDTarget object is member of
7140          * @param {Object} config The DDTarget config object
7141          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7142          * @return {Roo.dd.DDTarget} The DDTarget object
7143          */
7144         initDDTarget : function(group, config, overrides){
7145             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7146             return Roo.apply(dd, overrides);
7147         },
7148
7149         /**
7150          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7151          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7152          * @param {Boolean} visible Whether the element is visible
7153          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7154          * @return {Roo.Element} this
7155          */
7156          setVisible : function(visible, animate){
7157             if(!animate || !A){
7158                 if(this.visibilityMode == El.DISPLAY){
7159                     this.setDisplayed(visible);
7160                 }else{
7161                     this.fixDisplay();
7162                     this.dom.style.visibility = visible ? "visible" : "hidden";
7163                 }
7164             }else{
7165                 // closure for composites
7166                 var dom = this.dom;
7167                 var visMode = this.visibilityMode;
7168                 if(visible){
7169                     this.setOpacity(.01);
7170                     this.setVisible(true);
7171                 }
7172                 this.anim({opacity: { to: (visible?1:0) }},
7173                       this.preanim(arguments, 1),
7174                       null, .35, 'easeIn', function(){
7175                          if(!visible){
7176                              if(visMode == El.DISPLAY){
7177                                  dom.style.display = "none";
7178                              }else{
7179                                  dom.style.visibility = "hidden";
7180                              }
7181                              Roo.get(dom).setOpacity(1);
7182                          }
7183                      });
7184             }
7185             return this;
7186         },
7187
7188         /**
7189          * Returns true if display is not "none"
7190          * @return {Boolean}
7191          */
7192         isDisplayed : function() {
7193             return this.getStyle("display") != "none";
7194         },
7195
7196         /**
7197          * Toggles the element's visibility or display, depending on visibility mode.
7198          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7199          * @return {Roo.Element} this
7200          */
7201         toggle : function(animate){
7202             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7203             return this;
7204         },
7205
7206         /**
7207          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7208          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7209          * @return {Roo.Element} this
7210          */
7211         setDisplayed : function(value) {
7212             if(typeof value == "boolean"){
7213                value = value ? this.originalDisplay : "none";
7214             }
7215             this.setStyle("display", value);
7216             return this;
7217         },
7218
7219         /**
7220          * Tries to focus the element. Any exceptions are caught and ignored.
7221          * @return {Roo.Element} this
7222          */
7223         focus : function() {
7224             try{
7225                 this.dom.focus();
7226             }catch(e){}
7227             return this;
7228         },
7229
7230         /**
7231          * Tries to blur the element. Any exceptions are caught and ignored.
7232          * @return {Roo.Element} this
7233          */
7234         blur : function() {
7235             try{
7236                 this.dom.blur();
7237             }catch(e){}
7238             return this;
7239         },
7240
7241         /**
7242          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7243          * @param {String/Array} className The CSS class to add, or an array of classes
7244          * @return {Roo.Element} this
7245          */
7246         addClass : function(className){
7247             if(className instanceof Array){
7248                 for(var i = 0, len = className.length; i < len; i++) {
7249                     this.addClass(className[i]);
7250                 }
7251             }else{
7252                 if(className && !this.hasClass(className)){
7253                     this.dom.className = this.dom.className + " " + className;
7254                 }
7255             }
7256             return this;
7257         },
7258
7259         /**
7260          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7261          * @param {String/Array} className The CSS class to add, or an array of classes
7262          * @return {Roo.Element} this
7263          */
7264         radioClass : function(className){
7265             var siblings = this.dom.parentNode.childNodes;
7266             for(var i = 0; i < siblings.length; i++) {
7267                 var s = siblings[i];
7268                 if(s.nodeType == 1){
7269                     Roo.get(s).removeClass(className);
7270                 }
7271             }
7272             this.addClass(className);
7273             return this;
7274         },
7275
7276         /**
7277          * Removes one or more CSS classes from the element.
7278          * @param {String/Array} className The CSS class to remove, or an array of classes
7279          * @return {Roo.Element} this
7280          */
7281         removeClass : function(className){
7282             if(!className || !this.dom.className){
7283                 return this;
7284             }
7285             if(className instanceof Array){
7286                 for(var i = 0, len = className.length; i < len; i++) {
7287                     this.removeClass(className[i]);
7288                 }
7289             }else{
7290                 if(this.hasClass(className)){
7291                     var re = this.classReCache[className];
7292                     if (!re) {
7293                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7294                        this.classReCache[className] = re;
7295                     }
7296                     this.dom.className =
7297                         this.dom.className.replace(re, " ");
7298                 }
7299             }
7300             return this;
7301         },
7302
7303         // private
7304         classReCache: {},
7305
7306         /**
7307          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7308          * @param {String} className The CSS class to toggle
7309          * @return {Roo.Element} this
7310          */
7311         toggleClass : function(className){
7312             if(this.hasClass(className)){
7313                 this.removeClass(className);
7314             }else{
7315                 this.addClass(className);
7316             }
7317             return this;
7318         },
7319
7320         /**
7321          * Checks if the specified CSS class exists on this element's DOM node.
7322          * @param {String} className The CSS class to check for
7323          * @return {Boolean} True if the class exists, else false
7324          */
7325         hasClass : function(className){
7326             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7327         },
7328
7329         /**
7330          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7331          * @param {String} oldClassName The CSS class to replace
7332          * @param {String} newClassName The replacement CSS class
7333          * @return {Roo.Element} this
7334          */
7335         replaceClass : function(oldClassName, newClassName){
7336             this.removeClass(oldClassName);
7337             this.addClass(newClassName);
7338             return this;
7339         },
7340
7341         /**
7342          * Returns an object with properties matching the styles requested.
7343          * For example, el.getStyles('color', 'font-size', 'width') might return
7344          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7345          * @param {String} style1 A style name
7346          * @param {String} style2 A style name
7347          * @param {String} etc.
7348          * @return {Object} The style object
7349          */
7350         getStyles : function(){
7351             var a = arguments, len = a.length, r = {};
7352             for(var i = 0; i < len; i++){
7353                 r[a[i]] = this.getStyle(a[i]);
7354             }
7355             return r;
7356         },
7357
7358         /**
7359          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7360          * @param {String} property The style property whose value is returned.
7361          * @return {String} The current value of the style property for this element.
7362          */
7363         getStyle : function(){
7364             return view && view.getComputedStyle ?
7365                 function(prop){
7366                     var el = this.dom, v, cs, camel;
7367                     if(prop == 'float'){
7368                         prop = "cssFloat";
7369                     }
7370                     if(el.style && (v = el.style[prop])){
7371                         return v;
7372                     }
7373                     if(cs = view.getComputedStyle(el, "")){
7374                         if(!(camel = propCache[prop])){
7375                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7376                         }
7377                         return cs[camel];
7378                     }
7379                     return null;
7380                 } :
7381                 function(prop){
7382                     var el = this.dom, v, cs, camel;
7383                     if(prop == 'opacity'){
7384                         if(typeof el.style.filter == 'string'){
7385                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7386                             if(m){
7387                                 var fv = parseFloat(m[1]);
7388                                 if(!isNaN(fv)){
7389                                     return fv ? fv / 100 : 0;
7390                                 }
7391                             }
7392                         }
7393                         return 1;
7394                     }else if(prop == 'float'){
7395                         prop = "styleFloat";
7396                     }
7397                     if(!(camel = propCache[prop])){
7398                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7399                     }
7400                     if(v = el.style[camel]){
7401                         return v;
7402                     }
7403                     if(cs = el.currentStyle){
7404                         return cs[camel];
7405                     }
7406                     return null;
7407                 };
7408         }(),
7409
7410         /**
7411          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7412          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7413          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7414          * @return {Roo.Element} this
7415          */
7416         setStyle : function(prop, value){
7417             if(typeof prop == "string"){
7418                 
7419                 if (prop == 'float') {
7420                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7421                     return this;
7422                 }
7423                 
7424                 var camel;
7425                 if(!(camel = propCache[prop])){
7426                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7427                 }
7428                 
7429                 if(camel == 'opacity') {
7430                     this.setOpacity(value);
7431                 }else{
7432                     this.dom.style[camel] = value;
7433                 }
7434             }else{
7435                 for(var style in prop){
7436                     if(typeof prop[style] != "function"){
7437                        this.setStyle(style, prop[style]);
7438                     }
7439                 }
7440             }
7441             return this;
7442         },
7443
7444         /**
7445          * More flexible version of {@link #setStyle} for setting style properties.
7446          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7447          * a function which returns such a specification.
7448          * @return {Roo.Element} this
7449          */
7450         applyStyles : function(style){
7451             Roo.DomHelper.applyStyles(this.dom, style);
7452             return this;
7453         },
7454
7455         /**
7456           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7457           * @return {Number} The X position of the element
7458           */
7459         getX : function(){
7460             return D.getX(this.dom);
7461         },
7462
7463         /**
7464           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7465           * @return {Number} The Y position of the element
7466           */
7467         getY : function(){
7468             return D.getY(this.dom);
7469         },
7470
7471         /**
7472           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7473           * @return {Array} The XY position of the element
7474           */
7475         getXY : function(){
7476             return D.getXY(this.dom);
7477         },
7478
7479         /**
7480          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7481          * @param {Number} The X position of the element
7482          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7483          * @return {Roo.Element} this
7484          */
7485         setX : function(x, animate){
7486             if(!animate || !A){
7487                 D.setX(this.dom, x);
7488             }else{
7489                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7490             }
7491             return this;
7492         },
7493
7494         /**
7495          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7496          * @param {Number} The Y position of the element
7497          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7498          * @return {Roo.Element} this
7499          */
7500         setY : function(y, animate){
7501             if(!animate || !A){
7502                 D.setY(this.dom, y);
7503             }else{
7504                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7505             }
7506             return this;
7507         },
7508
7509         /**
7510          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7511          * @param {String} left The left CSS property value
7512          * @return {Roo.Element} this
7513          */
7514         setLeft : function(left){
7515             this.setStyle("left", this.addUnits(left));
7516             return this;
7517         },
7518
7519         /**
7520          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7521          * @param {String} top The top CSS property value
7522          * @return {Roo.Element} this
7523          */
7524         setTop : function(top){
7525             this.setStyle("top", this.addUnits(top));
7526             return this;
7527         },
7528
7529         /**
7530          * Sets the element's CSS right style.
7531          * @param {String} right The right CSS property value
7532          * @return {Roo.Element} this
7533          */
7534         setRight : function(right){
7535             this.setStyle("right", this.addUnits(right));
7536             return this;
7537         },
7538
7539         /**
7540          * Sets the element's CSS bottom style.
7541          * @param {String} bottom The bottom CSS property value
7542          * @return {Roo.Element} this
7543          */
7544         setBottom : function(bottom){
7545             this.setStyle("bottom", this.addUnits(bottom));
7546             return this;
7547         },
7548
7549         /**
7550          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7551          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7552          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7553          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7554          * @return {Roo.Element} this
7555          */
7556         setXY : function(pos, animate){
7557             if(!animate || !A){
7558                 D.setXY(this.dom, pos);
7559             }else{
7560                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7561             }
7562             return this;
7563         },
7564
7565         /**
7566          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7567          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7568          * @param {Number} x X value for new position (coordinates are page-based)
7569          * @param {Number} y Y value for new position (coordinates are page-based)
7570          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7571          * @return {Roo.Element} this
7572          */
7573         setLocation : function(x, y, animate){
7574             this.setXY([x, y], this.preanim(arguments, 2));
7575             return this;
7576         },
7577
7578         /**
7579          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7580          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7581          * @param {Number} x X value for new position (coordinates are page-based)
7582          * @param {Number} y Y value for new position (coordinates are page-based)
7583          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7584          * @return {Roo.Element} this
7585          */
7586         moveTo : function(x, y, animate){
7587             this.setXY([x, y], this.preanim(arguments, 2));
7588             return this;
7589         },
7590
7591         /**
7592          * Returns the region of the given element.
7593          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7594          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7595          */
7596         getRegion : function(){
7597             return D.getRegion(this.dom);
7598         },
7599
7600         /**
7601          * Returns the offset height of the element
7602          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7603          * @return {Number} The element's height
7604          */
7605         getHeight : function(contentHeight){
7606             var h = this.dom.offsetHeight || 0;
7607             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7608         },
7609
7610         /**
7611          * Returns the offset width of the element
7612          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7613          * @return {Number} The element's width
7614          */
7615         getWidth : function(contentWidth){
7616             var w = this.dom.offsetWidth || 0;
7617             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7618         },
7619
7620         /**
7621          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7622          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7623          * if a height has not been set using CSS.
7624          * @return {Number}
7625          */
7626         getComputedHeight : function(){
7627             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7628             if(!h){
7629                 h = parseInt(this.getStyle('height'), 10) || 0;
7630                 if(!this.isBorderBox()){
7631                     h += this.getFrameWidth('tb');
7632                 }
7633             }
7634             return h;
7635         },
7636
7637         /**
7638          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7639          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7640          * if a width has not been set using CSS.
7641          * @return {Number}
7642          */
7643         getComputedWidth : function(){
7644             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7645             if(!w){
7646                 w = parseInt(this.getStyle('width'), 10) || 0;
7647                 if(!this.isBorderBox()){
7648                     w += this.getFrameWidth('lr');
7649                 }
7650             }
7651             return w;
7652         },
7653
7654         /**
7655          * Returns the size of the element.
7656          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7657          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7658          */
7659         getSize : function(contentSize){
7660             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7661         },
7662
7663         /**
7664          * Returns the width and height of the viewport.
7665          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7666          */
7667         getViewSize : function(){
7668             var d = this.dom, doc = document, aw = 0, ah = 0;
7669             if(d == doc || d == doc.body){
7670                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7671             }else{
7672                 return {
7673                     width : d.clientWidth,
7674                     height: d.clientHeight
7675                 };
7676             }
7677         },
7678
7679         /**
7680          * Returns the value of the "value" attribute
7681          * @param {Boolean} asNumber true to parse the value as a number
7682          * @return {String/Number}
7683          */
7684         getValue : function(asNumber){
7685             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7686         },
7687
7688         // private
7689         adjustWidth : function(width){
7690             if(typeof width == "number"){
7691                 if(this.autoBoxAdjust && !this.isBorderBox()){
7692                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7693                 }
7694                 if(width < 0){
7695                     width = 0;
7696                 }
7697             }
7698             return width;
7699         },
7700
7701         // private
7702         adjustHeight : function(height){
7703             if(typeof height == "number"){
7704                if(this.autoBoxAdjust && !this.isBorderBox()){
7705                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7706                }
7707                if(height < 0){
7708                    height = 0;
7709                }
7710             }
7711             return height;
7712         },
7713
7714         /**
7715          * Set the width of the element
7716          * @param {Number} width The new width
7717          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7718          * @return {Roo.Element} this
7719          */
7720         setWidth : function(width, animate){
7721             width = this.adjustWidth(width);
7722             if(!animate || !A){
7723                 this.dom.style.width = this.addUnits(width);
7724             }else{
7725                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7726             }
7727             return this;
7728         },
7729
7730         /**
7731          * Set the height of the element
7732          * @param {Number} height The new height
7733          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7734          * @return {Roo.Element} this
7735          */
7736          setHeight : function(height, animate){
7737             height = this.adjustHeight(height);
7738             if(!animate || !A){
7739                 this.dom.style.height = this.addUnits(height);
7740             }else{
7741                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7742             }
7743             return this;
7744         },
7745
7746         /**
7747          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7748          * @param {Number} width The new width
7749          * @param {Number} height The new height
7750          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7751          * @return {Roo.Element} this
7752          */
7753          setSize : function(width, height, animate){
7754             if(typeof width == "object"){ // in case of object from getSize()
7755                 height = width.height; width = width.width;
7756             }
7757             width = this.adjustWidth(width); height = this.adjustHeight(height);
7758             if(!animate || !A){
7759                 this.dom.style.width = this.addUnits(width);
7760                 this.dom.style.height = this.addUnits(height);
7761             }else{
7762                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7763             }
7764             return this;
7765         },
7766
7767         /**
7768          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7769          * @param {Number} x X value for new position (coordinates are page-based)
7770          * @param {Number} y Y value for new position (coordinates are page-based)
7771          * @param {Number} width The new width
7772          * @param {Number} height The new height
7773          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7774          * @return {Roo.Element} this
7775          */
7776         setBounds : function(x, y, width, height, animate){
7777             if(!animate || !A){
7778                 this.setSize(width, height);
7779                 this.setLocation(x, y);
7780             }else{
7781                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7782                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7783                               this.preanim(arguments, 4), 'motion');
7784             }
7785             return this;
7786         },
7787
7788         /**
7789          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7790          * @param {Roo.lib.Region} region The region to fill
7791          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7792          * @return {Roo.Element} this
7793          */
7794         setRegion : function(region, animate){
7795             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7796             return this;
7797         },
7798
7799         /**
7800          * Appends an event handler
7801          *
7802          * @param {String}   eventName     The type of event to append
7803          * @param {Function} fn        The method the event invokes
7804          * @param {Object} scope       (optional) The scope (this object) of the fn
7805          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7806          */
7807         addListener : function(eventName, fn, scope, options){
7808             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7809         },
7810
7811         /**
7812          * Removes an event handler from this element
7813          * @param {String} eventName the type of event to remove
7814          * @param {Function} fn the method the event invokes
7815          * @return {Roo.Element} this
7816          */
7817         removeListener : function(eventName, fn){
7818             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7819             return this;
7820         },
7821
7822         /**
7823          * Removes all previous added listeners from this element
7824          * @return {Roo.Element} this
7825          */
7826         removeAllListeners : function(){
7827             E.purgeElement(this.dom);
7828             return this;
7829         },
7830
7831         relayEvent : function(eventName, observable){
7832             this.on(eventName, function(e){
7833                 observable.fireEvent(eventName, e);
7834             });
7835         },
7836
7837         /**
7838          * Set the opacity of the element
7839          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7840          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7841          * @return {Roo.Element} this
7842          */
7843          setOpacity : function(opacity, animate){
7844             if(!animate || !A){
7845                 var s = this.dom.style;
7846                 if(Roo.isIE){
7847                     s.zoom = 1;
7848                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7849                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7850                 }else{
7851                     s.opacity = opacity;
7852                 }
7853             }else{
7854                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7855             }
7856             return this;
7857         },
7858
7859         /**
7860          * Gets the left X coordinate
7861          * @param {Boolean} local True to get the local css position instead of page coordinate
7862          * @return {Number}
7863          */
7864         getLeft : function(local){
7865             if(!local){
7866                 return this.getX();
7867             }else{
7868                 return parseInt(this.getStyle("left"), 10) || 0;
7869             }
7870         },
7871
7872         /**
7873          * Gets the right X coordinate of the element (element X position + element width)
7874          * @param {Boolean} local True to get the local css position instead of page coordinate
7875          * @return {Number}
7876          */
7877         getRight : function(local){
7878             if(!local){
7879                 return this.getX() + this.getWidth();
7880             }else{
7881                 return (this.getLeft(true) + this.getWidth()) || 0;
7882             }
7883         },
7884
7885         /**
7886          * Gets the top Y coordinate
7887          * @param {Boolean} local True to get the local css position instead of page coordinate
7888          * @return {Number}
7889          */
7890         getTop : function(local) {
7891             if(!local){
7892                 return this.getY();
7893             }else{
7894                 return parseInt(this.getStyle("top"), 10) || 0;
7895             }
7896         },
7897
7898         /**
7899          * Gets the bottom Y coordinate of the element (element Y position + element height)
7900          * @param {Boolean} local True to get the local css position instead of page coordinate
7901          * @return {Number}
7902          */
7903         getBottom : function(local){
7904             if(!local){
7905                 return this.getY() + this.getHeight();
7906             }else{
7907                 return (this.getTop(true) + this.getHeight()) || 0;
7908             }
7909         },
7910
7911         /**
7912         * Initializes positioning on this element. If a desired position is not passed, it will make the
7913         * the element positioned relative IF it is not already positioned.
7914         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7915         * @param {Number} zIndex (optional) The zIndex to apply
7916         * @param {Number} x (optional) Set the page X position
7917         * @param {Number} y (optional) Set the page Y position
7918         */
7919         position : function(pos, zIndex, x, y){
7920             if(!pos){
7921                if(this.getStyle('position') == 'static'){
7922                    this.setStyle('position', 'relative');
7923                }
7924             }else{
7925                 this.setStyle("position", pos);
7926             }
7927             if(zIndex){
7928                 this.setStyle("z-index", zIndex);
7929             }
7930             if(x !== undefined && y !== undefined){
7931                 this.setXY([x, y]);
7932             }else if(x !== undefined){
7933                 this.setX(x);
7934             }else if(y !== undefined){
7935                 this.setY(y);
7936             }
7937         },
7938
7939         /**
7940         * Clear positioning back to the default when the document was loaded
7941         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7942         * @return {Roo.Element} this
7943          */
7944         clearPositioning : function(value){
7945             value = value ||'';
7946             this.setStyle({
7947                 "left": value,
7948                 "right": value,
7949                 "top": value,
7950                 "bottom": value,
7951                 "z-index": "",
7952                 "position" : "static"
7953             });
7954             return this;
7955         },
7956
7957         /**
7958         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7959         * snapshot before performing an update and then restoring the element.
7960         * @return {Object}
7961         */
7962         getPositioning : function(){
7963             var l = this.getStyle("left");
7964             var t = this.getStyle("top");
7965             return {
7966                 "position" : this.getStyle("position"),
7967                 "left" : l,
7968                 "right" : l ? "" : this.getStyle("right"),
7969                 "top" : t,
7970                 "bottom" : t ? "" : this.getStyle("bottom"),
7971                 "z-index" : this.getStyle("z-index")
7972             };
7973         },
7974
7975         /**
7976          * Gets the width of the border(s) for the specified side(s)
7977          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7978          * passing lr would get the border (l)eft width + the border (r)ight width.
7979          * @return {Number} The width of the sides passed added together
7980          */
7981         getBorderWidth : function(side){
7982             return this.addStyles(side, El.borders);
7983         },
7984
7985         /**
7986          * Gets the width of the padding(s) for the specified side(s)
7987          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7988          * passing lr would get the padding (l)eft + the padding (r)ight.
7989          * @return {Number} The padding of the sides passed added together
7990          */
7991         getPadding : function(side){
7992             return this.addStyles(side, El.paddings);
7993         },
7994
7995         /**
7996         * Set positioning with an object returned by getPositioning().
7997         * @param {Object} posCfg
7998         * @return {Roo.Element} this
7999          */
8000         setPositioning : function(pc){
8001             this.applyStyles(pc);
8002             if(pc.right == "auto"){
8003                 this.dom.style.right = "";
8004             }
8005             if(pc.bottom == "auto"){
8006                 this.dom.style.bottom = "";
8007             }
8008             return this;
8009         },
8010
8011         // private
8012         fixDisplay : function(){
8013             if(this.getStyle("display") == "none"){
8014                 this.setStyle("visibility", "hidden");
8015                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8016                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8017                     this.setStyle("display", "block");
8018                 }
8019             }
8020         },
8021
8022         /**
8023          * Quick set left and top adding default units
8024          * @param {String} left The left CSS property value
8025          * @param {String} top The top CSS property value
8026          * @return {Roo.Element} this
8027          */
8028          setLeftTop : function(left, top){
8029             this.dom.style.left = this.addUnits(left);
8030             this.dom.style.top = this.addUnits(top);
8031             return this;
8032         },
8033
8034         /**
8035          * Move this element relative to its current position.
8036          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8037          * @param {Number} distance How far to move the element in pixels
8038          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8039          * @return {Roo.Element} this
8040          */
8041          move : function(direction, distance, animate){
8042             var xy = this.getXY();
8043             direction = direction.toLowerCase();
8044             switch(direction){
8045                 case "l":
8046                 case "left":
8047                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8048                     break;
8049                case "r":
8050                case "right":
8051                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8052                     break;
8053                case "t":
8054                case "top":
8055                case "up":
8056                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8057                     break;
8058                case "b":
8059                case "bottom":
8060                case "down":
8061                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8062                     break;
8063             }
8064             return this;
8065         },
8066
8067         /**
8068          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8069          * @return {Roo.Element} this
8070          */
8071         clip : function(){
8072             if(!this.isClipped){
8073                this.isClipped = true;
8074                this.originalClip = {
8075                    "o": this.getStyle("overflow"),
8076                    "x": this.getStyle("overflow-x"),
8077                    "y": this.getStyle("overflow-y")
8078                };
8079                this.setStyle("overflow", "hidden");
8080                this.setStyle("overflow-x", "hidden");
8081                this.setStyle("overflow-y", "hidden");
8082             }
8083             return this;
8084         },
8085
8086         /**
8087          *  Return clipping (overflow) to original clipping before clip() was called
8088          * @return {Roo.Element} this
8089          */
8090         unclip : function(){
8091             if(this.isClipped){
8092                 this.isClipped = false;
8093                 var o = this.originalClip;
8094                 if(o.o){this.setStyle("overflow", o.o);}
8095                 if(o.x){this.setStyle("overflow-x", o.x);}
8096                 if(o.y){this.setStyle("overflow-y", o.y);}
8097             }
8098             return this;
8099         },
8100
8101
8102         /**
8103          * Gets the x,y coordinates specified by the anchor position on the element.
8104          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8105          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8106          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8107          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8108          * @return {Array} [x, y] An array containing the element's x and y coordinates
8109          */
8110         getAnchorXY : function(anchor, local, s){
8111             //Passing a different size is useful for pre-calculating anchors,
8112             //especially for anchored animations that change the el size.
8113
8114             var w, h, vp = false;
8115             if(!s){
8116                 var d = this.dom;
8117                 if(d == document.body || d == document){
8118                     vp = true;
8119                     w = D.getViewWidth(); h = D.getViewHeight();
8120                 }else{
8121                     w = this.getWidth(); h = this.getHeight();
8122                 }
8123             }else{
8124                 w = s.width;  h = s.height;
8125             }
8126             var x = 0, y = 0, r = Math.round;
8127             switch((anchor || "tl").toLowerCase()){
8128                 case "c":
8129                     x = r(w*.5);
8130                     y = r(h*.5);
8131                 break;
8132                 case "t":
8133                     x = r(w*.5);
8134                     y = 0;
8135                 break;
8136                 case "l":
8137                     x = 0;
8138                     y = r(h*.5);
8139                 break;
8140                 case "r":
8141                     x = w;
8142                     y = r(h*.5);
8143                 break;
8144                 case "b":
8145                     x = r(w*.5);
8146                     y = h;
8147                 break;
8148                 case "tl":
8149                     x = 0;
8150                     y = 0;
8151                 break;
8152                 case "bl":
8153                     x = 0;
8154                     y = h;
8155                 break;
8156                 case "br":
8157                     x = w;
8158                     y = h;
8159                 break;
8160                 case "tr":
8161                     x = w;
8162                     y = 0;
8163                 break;
8164             }
8165             if(local === true){
8166                 return [x, y];
8167             }
8168             if(vp){
8169                 var sc = this.getScroll();
8170                 return [x + sc.left, y + sc.top];
8171             }
8172             //Add the element's offset xy
8173             var o = this.getXY();
8174             return [x+o[0], y+o[1]];
8175         },
8176
8177         /**
8178          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8179          * supported position values.
8180          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8181          * @param {String} position The position to align to.
8182          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8183          * @return {Array} [x, y]
8184          */
8185         getAlignToXY : function(el, p, o){
8186             el = Roo.get(el);
8187             var d = this.dom;
8188             if(!el.dom){
8189                 throw "Element.alignTo with an element that doesn't exist";
8190             }
8191             var c = false; //constrain to viewport
8192             var p1 = "", p2 = "";
8193             o = o || [0,0];
8194
8195             if(!p){
8196                 p = "tl-bl";
8197             }else if(p == "?"){
8198                 p = "tl-bl?";
8199             }else if(p.indexOf("-") == -1){
8200                 p = "tl-" + p;
8201             }
8202             p = p.toLowerCase();
8203             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8204             if(!m){
8205                throw "Element.alignTo with an invalid alignment " + p;
8206             }
8207             p1 = m[1]; p2 = m[2]; c = !!m[3];
8208
8209             //Subtract the aligned el's internal xy from the target's offset xy
8210             //plus custom offset to get the aligned el's new offset xy
8211             var a1 = this.getAnchorXY(p1, true);
8212             var a2 = el.getAnchorXY(p2, false);
8213             var x = a2[0] - a1[0] + o[0];
8214             var y = a2[1] - a1[1] + o[1];
8215             if(c){
8216                 //constrain the aligned el to viewport if necessary
8217                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8218                 // 5px of margin for ie
8219                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8220
8221                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8222                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8223                 //otherwise swap the aligned el to the opposite border of the target.
8224                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8225                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8226                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8227                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8228
8229                var doc = document;
8230                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8231                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8232
8233                if((x+w) > dw + scrollX){
8234                     x = swapX ? r.left-w : dw+scrollX-w;
8235                 }
8236                if(x < scrollX){
8237                    x = swapX ? r.right : scrollX;
8238                }
8239                if((y+h) > dh + scrollY){
8240                     y = swapY ? r.top-h : dh+scrollY-h;
8241                 }
8242                if (y < scrollY){
8243                    y = swapY ? r.bottom : scrollY;
8244                }
8245             }
8246             return [x,y];
8247         },
8248
8249         // private
8250         getConstrainToXY : function(){
8251             var os = {top:0, left:0, bottom:0, right: 0};
8252
8253             return function(el, local, offsets, proposedXY){
8254                 el = Roo.get(el);
8255                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8256
8257                 var vw, vh, vx = 0, vy = 0;
8258                 if(el.dom == document.body || el.dom == document){
8259                     vw = Roo.lib.Dom.getViewWidth();
8260                     vh = Roo.lib.Dom.getViewHeight();
8261                 }else{
8262                     vw = el.dom.clientWidth;
8263                     vh = el.dom.clientHeight;
8264                     if(!local){
8265                         var vxy = el.getXY();
8266                         vx = vxy[0];
8267                         vy = vxy[1];
8268                     }
8269                 }
8270
8271                 var s = el.getScroll();
8272
8273                 vx += offsets.left + s.left;
8274                 vy += offsets.top + s.top;
8275
8276                 vw -= offsets.right;
8277                 vh -= offsets.bottom;
8278
8279                 var vr = vx+vw;
8280                 var vb = vy+vh;
8281
8282                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8283                 var x = xy[0], y = xy[1];
8284                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8285
8286                 // only move it if it needs it
8287                 var moved = false;
8288
8289                 // first validate right/bottom
8290                 if((x + w) > vr){
8291                     x = vr - w;
8292                     moved = true;
8293                 }
8294                 if((y + h) > vb){
8295                     y = vb - h;
8296                     moved = true;
8297                 }
8298                 // then make sure top/left isn't negative
8299                 if(x < vx){
8300                     x = vx;
8301                     moved = true;
8302                 }
8303                 if(y < vy){
8304                     y = vy;
8305                     moved = true;
8306                 }
8307                 return moved ? [x, y] : false;
8308             };
8309         }(),
8310
8311         // private
8312         adjustForConstraints : function(xy, parent, offsets){
8313             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8314         },
8315
8316         /**
8317          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8318          * document it aligns it to the viewport.
8319          * The position parameter is optional, and can be specified in any one of the following formats:
8320          * <ul>
8321          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8322          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8323          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8324          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8325          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8326          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8327          * </ul>
8328          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8329          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8330          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8331          * that specified in order to enforce the viewport constraints.
8332          * Following are all of the supported anchor positions:
8333     <pre>
8334     Value  Description
8335     -----  -----------------------------
8336     tl     The top left corner (default)
8337     t      The center of the top edge
8338     tr     The top right corner
8339     l      The center of the left edge
8340     c      In the center of the element
8341     r      The center of the right edge
8342     bl     The bottom left corner
8343     b      The center of the bottom edge
8344     br     The bottom right corner
8345     </pre>
8346     Example Usage:
8347     <pre><code>
8348     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8349     el.alignTo("other-el");
8350
8351     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8352     el.alignTo("other-el", "tr?");
8353
8354     // align the bottom right corner of el with the center left edge of other-el
8355     el.alignTo("other-el", "br-l?");
8356
8357     // align the center of el with the bottom left corner of other-el and
8358     // adjust the x position by -6 pixels (and the y position by 0)
8359     el.alignTo("other-el", "c-bl", [-6, 0]);
8360     </code></pre>
8361          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8362          * @param {String} position The position to align to.
8363          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8364          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8365          * @return {Roo.Element} this
8366          */
8367         alignTo : function(element, position, offsets, animate){
8368             var xy = this.getAlignToXY(element, position, offsets);
8369             this.setXY(xy, this.preanim(arguments, 3));
8370             return this;
8371         },
8372
8373         /**
8374          * Anchors an element to another element and realigns it when the window is resized.
8375          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8376          * @param {String} position The position to align to.
8377          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8378          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8379          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8380          * is a number, it is used as the buffer delay (defaults to 50ms).
8381          * @param {Function} callback The function to call after the animation finishes
8382          * @return {Roo.Element} this
8383          */
8384         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8385             var action = function(){
8386                 this.alignTo(el, alignment, offsets, animate);
8387                 Roo.callback(callback, this);
8388             };
8389             Roo.EventManager.onWindowResize(action, this);
8390             var tm = typeof monitorScroll;
8391             if(tm != 'undefined'){
8392                 Roo.EventManager.on(window, 'scroll', action, this,
8393                     {buffer: tm == 'number' ? monitorScroll : 50});
8394             }
8395             action.call(this); // align immediately
8396             return this;
8397         },
8398         /**
8399          * Clears any opacity settings from this element. Required in some cases for IE.
8400          * @return {Roo.Element} this
8401          */
8402         clearOpacity : function(){
8403             if (window.ActiveXObject) {
8404                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8405                     this.dom.style.filter = "";
8406                 }
8407             } else {
8408                 this.dom.style.opacity = "";
8409                 this.dom.style["-moz-opacity"] = "";
8410                 this.dom.style["-khtml-opacity"] = "";
8411             }
8412             return this;
8413         },
8414
8415         /**
8416          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8417          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8418          * @return {Roo.Element} this
8419          */
8420         hide : function(animate){
8421             this.setVisible(false, this.preanim(arguments, 0));
8422             return this;
8423         },
8424
8425         /**
8426         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8427         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8428          * @return {Roo.Element} this
8429          */
8430         show : function(animate){
8431             this.setVisible(true, this.preanim(arguments, 0));
8432             return this;
8433         },
8434
8435         /**
8436          * @private Test if size has a unit, otherwise appends the default
8437          */
8438         addUnits : function(size){
8439             return Roo.Element.addUnits(size, this.defaultUnit);
8440         },
8441
8442         /**
8443          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8444          * @return {Roo.Element} this
8445          */
8446         beginMeasure : function(){
8447             var el = this.dom;
8448             if(el.offsetWidth || el.offsetHeight){
8449                 return this; // offsets work already
8450             }
8451             var changed = [];
8452             var p = this.dom, b = document.body; // start with this element
8453             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8454                 var pe = Roo.get(p);
8455                 if(pe.getStyle('display') == 'none'){
8456                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8457                     p.style.visibility = "hidden";
8458                     p.style.display = "block";
8459                 }
8460                 p = p.parentNode;
8461             }
8462             this._measureChanged = changed;
8463             return this;
8464
8465         },
8466
8467         /**
8468          * Restores displays to before beginMeasure was called
8469          * @return {Roo.Element} this
8470          */
8471         endMeasure : function(){
8472             var changed = this._measureChanged;
8473             if(changed){
8474                 for(var i = 0, len = changed.length; i < len; i++) {
8475                     var r = changed[i];
8476                     r.el.style.visibility = r.visibility;
8477                     r.el.style.display = "none";
8478                 }
8479                 this._measureChanged = null;
8480             }
8481             return this;
8482         },
8483
8484         /**
8485         * Update the innerHTML of this element, optionally searching for and processing scripts
8486         * @param {String} html The new HTML
8487         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8488         * @param {Function} callback For async script loading you can be noticed when the update completes
8489         * @return {Roo.Element} this
8490          */
8491         update : function(html, loadScripts, callback){
8492             if(typeof html == "undefined"){
8493                 html = "";
8494             }
8495             if(loadScripts !== true){
8496                 this.dom.innerHTML = html;
8497                 if(typeof callback == "function"){
8498                     callback();
8499                 }
8500                 return this;
8501             }
8502             var id = Roo.id();
8503             var dom = this.dom;
8504
8505             html += '<span id="' + id + '"></span>';
8506
8507             E.onAvailable(id, function(){
8508                 var hd = document.getElementsByTagName("head")[0];
8509                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8510                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8511                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8512
8513                 var match;
8514                 while(match = re.exec(html)){
8515                     var attrs = match[1];
8516                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8517                     if(srcMatch && srcMatch[2]){
8518                        var s = document.createElement("script");
8519                        s.src = srcMatch[2];
8520                        var typeMatch = attrs.match(typeRe);
8521                        if(typeMatch && typeMatch[2]){
8522                            s.type = typeMatch[2];
8523                        }
8524                        hd.appendChild(s);
8525                     }else if(match[2] && match[2].length > 0){
8526                         if(window.execScript) {
8527                            window.execScript(match[2]);
8528                         } else {
8529                             /**
8530                              * eval:var:id
8531                              * eval:var:dom
8532                              * eval:var:html
8533                              * 
8534                              */
8535                            window.eval(match[2]);
8536                         }
8537                     }
8538                 }
8539                 var el = document.getElementById(id);
8540                 if(el){el.parentNode.removeChild(el);}
8541                 if(typeof callback == "function"){
8542                     callback();
8543                 }
8544             });
8545             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8546             return this;
8547         },
8548
8549         /**
8550          * Direct access to the UpdateManager update() method (takes the same parameters).
8551          * @param {String/Function} url The url for this request or a function to call to get the url
8552          * @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}
8553          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8554          * @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.
8555          * @return {Roo.Element} this
8556          */
8557         load : function(){
8558             var um = this.getUpdateManager();
8559             um.update.apply(um, arguments);
8560             return this;
8561         },
8562
8563         /**
8564         * Gets this element's UpdateManager
8565         * @return {Roo.UpdateManager} The UpdateManager
8566         */
8567         getUpdateManager : function(){
8568             if(!this.updateManager){
8569                 this.updateManager = new Roo.UpdateManager(this);
8570             }
8571             return this.updateManager;
8572         },
8573
8574         /**
8575          * Disables text selection for this element (normalized across browsers)
8576          * @return {Roo.Element} this
8577          */
8578         unselectable : function(){
8579             this.dom.unselectable = "on";
8580             this.swallowEvent("selectstart", true);
8581             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8582             this.addClass("x-unselectable");
8583             return this;
8584         },
8585
8586         /**
8587         * Calculates the x, y to center this element on the screen
8588         * @return {Array} The x, y values [x, y]
8589         */
8590         getCenterXY : function(){
8591             return this.getAlignToXY(document, 'c-c');
8592         },
8593
8594         /**
8595         * Centers the Element in either the viewport, or another Element.
8596         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8597         */
8598         center : function(centerIn){
8599             this.alignTo(centerIn || document, 'c-c');
8600             return this;
8601         },
8602
8603         /**
8604          * Tests various css rules/browsers to determine if this element uses a border box
8605          * @return {Boolean}
8606          */
8607         isBorderBox : function(){
8608             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8609         },
8610
8611         /**
8612          * Return a box {x, y, width, height} that can be used to set another elements
8613          * size/location to match this element.
8614          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8615          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8616          * @return {Object} box An object in the format {x, y, width, height}
8617          */
8618         getBox : function(contentBox, local){
8619             var xy;
8620             if(!local){
8621                 xy = this.getXY();
8622             }else{
8623                 var left = parseInt(this.getStyle("left"), 10) || 0;
8624                 var top = parseInt(this.getStyle("top"), 10) || 0;
8625                 xy = [left, top];
8626             }
8627             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8628             if(!contentBox){
8629                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8630             }else{
8631                 var l = this.getBorderWidth("l")+this.getPadding("l");
8632                 var r = this.getBorderWidth("r")+this.getPadding("r");
8633                 var t = this.getBorderWidth("t")+this.getPadding("t");
8634                 var b = this.getBorderWidth("b")+this.getPadding("b");
8635                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8636             }
8637             bx.right = bx.x + bx.width;
8638             bx.bottom = bx.y + bx.height;
8639             return bx;
8640         },
8641
8642         /**
8643          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8644          for more information about the sides.
8645          * @param {String} sides
8646          * @return {Number}
8647          */
8648         getFrameWidth : function(sides, onlyContentBox){
8649             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8650         },
8651
8652         /**
8653          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8654          * @param {Object} box The box to fill {x, y, width, height}
8655          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8656          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8657          * @return {Roo.Element} this
8658          */
8659         setBox : function(box, adjust, animate){
8660             var w = box.width, h = box.height;
8661             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8662                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8663                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8664             }
8665             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8666             return this;
8667         },
8668
8669         /**
8670          * Forces the browser to repaint this element
8671          * @return {Roo.Element} this
8672          */
8673          repaint : function(){
8674             var dom = this.dom;
8675             this.addClass("x-repaint");
8676             setTimeout(function(){
8677                 Roo.get(dom).removeClass("x-repaint");
8678             }, 1);
8679             return this;
8680         },
8681
8682         /**
8683          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8684          * then it returns the calculated width of the sides (see getPadding)
8685          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8686          * @return {Object/Number}
8687          */
8688         getMargins : function(side){
8689             if(!side){
8690                 return {
8691                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8692                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8693                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8694                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8695                 };
8696             }else{
8697                 return this.addStyles(side, El.margins);
8698              }
8699         },
8700
8701         // private
8702         addStyles : function(sides, styles){
8703             var val = 0, v, w;
8704             for(var i = 0, len = sides.length; i < len; i++){
8705                 v = this.getStyle(styles[sides.charAt(i)]);
8706                 if(v){
8707                      w = parseInt(v, 10);
8708                      if(w){ val += w; }
8709                 }
8710             }
8711             return val;
8712         },
8713
8714         /**
8715          * Creates a proxy element of this element
8716          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8717          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8718          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8719          * @return {Roo.Element} The new proxy element
8720          */
8721         createProxy : function(config, renderTo, matchBox){
8722             if(renderTo){
8723                 renderTo = Roo.getDom(renderTo);
8724             }else{
8725                 renderTo = document.body;
8726             }
8727             config = typeof config == "object" ?
8728                 config : {tag : "div", cls: config};
8729             var proxy = Roo.DomHelper.append(renderTo, config, true);
8730             if(matchBox){
8731                proxy.setBox(this.getBox());
8732             }
8733             return proxy;
8734         },
8735
8736         /**
8737          * Puts a mask over this element to disable user interaction. Requires core.css.
8738          * This method can only be applied to elements which accept child nodes.
8739          * @param {String} msg (optional) A message to display in the mask
8740          * @param {String} msgCls (optional) A css class to apply to the msg element
8741          * @return {Element} The mask  element
8742          */
8743         mask : function(msg, msgCls){
8744             if(this.getStyle("position") == "static"){
8745                 this.setStyle("position", "relative");
8746             }
8747             if(!this._mask){
8748                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8749             }
8750             this.addClass("x-masked");
8751             this._mask.setDisplayed(true);
8752             if(typeof msg == 'string'){
8753                 if(!this._maskMsg){
8754                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8755                 }
8756                 var mm = this._maskMsg;
8757                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8758                 mm.dom.firstChild.innerHTML = msg;
8759                 mm.setDisplayed(true);
8760                 mm.center(this);
8761             }
8762             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8763                 this._mask.setHeight(this.getHeight());
8764             }
8765             return this._mask;
8766         },
8767
8768         /**
8769          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8770          * it is cached for reuse.
8771          */
8772         unmask : function(removeEl){
8773             if(this._mask){
8774                 if(removeEl === true){
8775                     this._mask.remove();
8776                     delete this._mask;
8777                     if(this._maskMsg){
8778                         this._maskMsg.remove();
8779                         delete this._maskMsg;
8780                     }
8781                 }else{
8782                     this._mask.setDisplayed(false);
8783                     if(this._maskMsg){
8784                         this._maskMsg.setDisplayed(false);
8785                     }
8786                 }
8787             }
8788             this.removeClass("x-masked");
8789         },
8790
8791         /**
8792          * Returns true if this element is masked
8793          * @return {Boolean}
8794          */
8795         isMasked : function(){
8796             return this._mask && this._mask.isVisible();
8797         },
8798
8799         /**
8800          * Creates an iframe shim for this element to keep selects and other windowed objects from
8801          * showing through.
8802          * @return {Roo.Element} The new shim element
8803          */
8804         createShim : function(){
8805             var el = document.createElement('iframe');
8806             el.frameBorder = 'no';
8807             el.className = 'roo-shim';
8808             if(Roo.isIE && Roo.isSecure){
8809                 el.src = Roo.SSL_SECURE_URL;
8810             }
8811             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8812             shim.autoBoxAdjust = false;
8813             return shim;
8814         },
8815
8816         /**
8817          * Removes this element from the DOM and deletes it from the cache
8818          */
8819         remove : function(){
8820             if(this.dom.parentNode){
8821                 this.dom.parentNode.removeChild(this.dom);
8822             }
8823             delete El.cache[this.dom.id];
8824         },
8825
8826         /**
8827          * Sets up event handlers to add and remove a css class when the mouse is over this element
8828          * @param {String} className
8829          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8830          * mouseout events for children elements
8831          * @return {Roo.Element} this
8832          */
8833         addClassOnOver : function(className, preventFlicker){
8834             this.on("mouseover", function(){
8835                 Roo.fly(this, '_internal').addClass(className);
8836             }, this.dom);
8837             var removeFn = function(e){
8838                 if(preventFlicker !== true || !e.within(this, true)){
8839                     Roo.fly(this, '_internal').removeClass(className);
8840                 }
8841             };
8842             this.on("mouseout", removeFn, this.dom);
8843             return this;
8844         },
8845
8846         /**
8847          * Sets up event handlers to add and remove a css class when this element has the focus
8848          * @param {String} className
8849          * @return {Roo.Element} this
8850          */
8851         addClassOnFocus : function(className){
8852             this.on("focus", function(){
8853                 Roo.fly(this, '_internal').addClass(className);
8854             }, this.dom);
8855             this.on("blur", function(){
8856                 Roo.fly(this, '_internal').removeClass(className);
8857             }, this.dom);
8858             return this;
8859         },
8860         /**
8861          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8862          * @param {String} className
8863          * @return {Roo.Element} this
8864          */
8865         addClassOnClick : function(className){
8866             var dom = this.dom;
8867             this.on("mousedown", function(){
8868                 Roo.fly(dom, '_internal').addClass(className);
8869                 var d = Roo.get(document);
8870                 var fn = function(){
8871                     Roo.fly(dom, '_internal').removeClass(className);
8872                     d.removeListener("mouseup", fn);
8873                 };
8874                 d.on("mouseup", fn);
8875             });
8876             return this;
8877         },
8878
8879         /**
8880          * Stops the specified event from bubbling and optionally prevents the default action
8881          * @param {String} eventName
8882          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8883          * @return {Roo.Element} this
8884          */
8885         swallowEvent : function(eventName, preventDefault){
8886             var fn = function(e){
8887                 e.stopPropagation();
8888                 if(preventDefault){
8889                     e.preventDefault();
8890                 }
8891             };
8892             if(eventName instanceof Array){
8893                 for(var i = 0, len = eventName.length; i < len; i++){
8894                      this.on(eventName[i], fn);
8895                 }
8896                 return this;
8897             }
8898             this.on(eventName, fn);
8899             return this;
8900         },
8901
8902         /**
8903          * @private
8904          */
8905       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8906
8907         /**
8908          * Sizes this element to its parent element's dimensions performing
8909          * neccessary box adjustments.
8910          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8911          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8912          * @return {Roo.Element} this
8913          */
8914         fitToParent : function(monitorResize, targetParent) {
8915           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8916           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8917           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8918             return;
8919           }
8920           var p = Roo.get(targetParent || this.dom.parentNode);
8921           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8922           if (monitorResize === true) {
8923             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8924             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8925           }
8926           return this;
8927         },
8928
8929         /**
8930          * Gets the next sibling, skipping text nodes
8931          * @return {HTMLElement} The next sibling or null
8932          */
8933         getNextSibling : function(){
8934             var n = this.dom.nextSibling;
8935             while(n && n.nodeType != 1){
8936                 n = n.nextSibling;
8937             }
8938             return n;
8939         },
8940
8941         /**
8942          * Gets the previous sibling, skipping text nodes
8943          * @return {HTMLElement} The previous sibling or null
8944          */
8945         getPrevSibling : function(){
8946             var n = this.dom.previousSibling;
8947             while(n && n.nodeType != 1){
8948                 n = n.previousSibling;
8949             }
8950             return n;
8951         },
8952
8953
8954         /**
8955          * Appends the passed element(s) to this element
8956          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8957          * @return {Roo.Element} this
8958          */
8959         appendChild: function(el){
8960             el = Roo.get(el);
8961             el.appendTo(this);
8962             return this;
8963         },
8964
8965         /**
8966          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8967          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8968          * automatically generated with the specified attributes.
8969          * @param {HTMLElement} insertBefore (optional) a child element of this element
8970          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8971          * @return {Roo.Element} The new child element
8972          */
8973         createChild: function(config, insertBefore, returnDom){
8974             config = config || {tag:'div'};
8975             if(insertBefore){
8976                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8977             }
8978             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8979         },
8980
8981         /**
8982          * Appends this element to the passed element
8983          * @param {String/HTMLElement/Element} el The new parent element
8984          * @return {Roo.Element} this
8985          */
8986         appendTo: function(el){
8987             el = Roo.getDom(el);
8988             el.appendChild(this.dom);
8989             return this;
8990         },
8991
8992         /**
8993          * Inserts this element before the passed element in the DOM
8994          * @param {String/HTMLElement/Element} el The element to insert before
8995          * @return {Roo.Element} this
8996          */
8997         insertBefore: function(el){
8998             el = Roo.getDom(el);
8999             el.parentNode.insertBefore(this.dom, el);
9000             return this;
9001         },
9002
9003         /**
9004          * Inserts this element after the passed element in the DOM
9005          * @param {String/HTMLElement/Element} el The element to insert after
9006          * @return {Roo.Element} this
9007          */
9008         insertAfter: function(el){
9009             el = Roo.getDom(el);
9010             el.parentNode.insertBefore(this.dom, el.nextSibling);
9011             return this;
9012         },
9013
9014         /**
9015          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9016          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9017          * @return {Roo.Element} The new child
9018          */
9019         insertFirst: function(el, returnDom){
9020             el = el || {};
9021             if(typeof el == 'object' && !el.nodeType){ // dh config
9022                 return this.createChild(el, this.dom.firstChild, returnDom);
9023             }else{
9024                 el = Roo.getDom(el);
9025                 this.dom.insertBefore(el, this.dom.firstChild);
9026                 return !returnDom ? Roo.get(el) : el;
9027             }
9028         },
9029
9030         /**
9031          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9032          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9033          * @param {String} where (optional) 'before' or 'after' defaults to before
9034          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9035          * @return {Roo.Element} the inserted Element
9036          */
9037         insertSibling: function(el, where, returnDom){
9038             where = where ? where.toLowerCase() : 'before';
9039             el = el || {};
9040             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9041
9042             if(typeof el == 'object' && !el.nodeType){ // dh config
9043                 if(where == 'after' && !this.dom.nextSibling){
9044                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9045                 }else{
9046                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9047                 }
9048
9049             }else{
9050                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9051                             where == 'before' ? this.dom : this.dom.nextSibling);
9052                 if(!returnDom){
9053                     rt = Roo.get(rt);
9054                 }
9055             }
9056             return rt;
9057         },
9058
9059         /**
9060          * Creates and wraps this element with another element
9061          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9062          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9063          * @return {HTMLElement/Element} The newly created wrapper element
9064          */
9065         wrap: function(config, returnDom){
9066             if(!config){
9067                 config = {tag: "div"};
9068             }
9069             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9070             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9071             return newEl;
9072         },
9073
9074         /**
9075          * Replaces the passed element with this element
9076          * @param {String/HTMLElement/Element} el The element to replace
9077          * @return {Roo.Element} this
9078          */
9079         replace: function(el){
9080             el = Roo.get(el);
9081             this.insertBefore(el);
9082             el.remove();
9083             return this;
9084         },
9085
9086         /**
9087          * Inserts an html fragment into this element
9088          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9089          * @param {String} html The HTML fragment
9090          * @param {Boolean} returnEl True to return an Roo.Element
9091          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9092          */
9093         insertHtml : function(where, html, returnEl){
9094             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9095             return returnEl ? Roo.get(el) : el;
9096         },
9097
9098         /**
9099          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9100          * @param {Object} o The object with the attributes
9101          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9102          * @return {Roo.Element} this
9103          */
9104         set : function(o, useSet){
9105             var el = this.dom;
9106             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9107             for(var attr in o){
9108                 if(attr == "style" || typeof o[attr] == "function") continue;
9109                 if(attr=="cls"){
9110                     el.className = o["cls"];
9111                 }else{
9112                     if(useSet) el.setAttribute(attr, o[attr]);
9113                     else el[attr] = o[attr];
9114                 }
9115             }
9116             if(o.style){
9117                 Roo.DomHelper.applyStyles(el, o.style);
9118             }
9119             return this;
9120         },
9121
9122         /**
9123          * Convenience method for constructing a KeyMap
9124          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9125          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9126          * @param {Function} fn The function to call
9127          * @param {Object} scope (optional) The scope of the function
9128          * @return {Roo.KeyMap} The KeyMap created
9129          */
9130         addKeyListener : function(key, fn, scope){
9131             var config;
9132             if(typeof key != "object" || key instanceof Array){
9133                 config = {
9134                     key: key,
9135                     fn: fn,
9136                     scope: scope
9137                 };
9138             }else{
9139                 config = {
9140                     key : key.key,
9141                     shift : key.shift,
9142                     ctrl : key.ctrl,
9143                     alt : key.alt,
9144                     fn: fn,
9145                     scope: scope
9146                 };
9147             }
9148             return new Roo.KeyMap(this, config);
9149         },
9150
9151         /**
9152          * Creates a KeyMap for this element
9153          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9154          * @return {Roo.KeyMap} The KeyMap created
9155          */
9156         addKeyMap : function(config){
9157             return new Roo.KeyMap(this, config);
9158         },
9159
9160         /**
9161          * Returns true if this element is scrollable.
9162          * @return {Boolean}
9163          */
9164          isScrollable : function(){
9165             var dom = this.dom;
9166             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9167         },
9168
9169         /**
9170          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9171          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9172          * @param {Number} value The new scroll value
9173          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9174          * @return {Element} this
9175          */
9176
9177         scrollTo : function(side, value, animate){
9178             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9179             if(!animate || !A){
9180                 this.dom[prop] = value;
9181             }else{
9182                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9183                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9184             }
9185             return this;
9186         },
9187
9188         /**
9189          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9190          * within this element's scrollable range.
9191          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9192          * @param {Number} distance How far to scroll the element in pixels
9193          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9194          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9195          * was scrolled as far as it could go.
9196          */
9197          scroll : function(direction, distance, animate){
9198              if(!this.isScrollable()){
9199                  return;
9200              }
9201              var el = this.dom;
9202              var l = el.scrollLeft, t = el.scrollTop;
9203              var w = el.scrollWidth, h = el.scrollHeight;
9204              var cw = el.clientWidth, ch = el.clientHeight;
9205              direction = direction.toLowerCase();
9206              var scrolled = false;
9207              var a = this.preanim(arguments, 2);
9208              switch(direction){
9209                  case "l":
9210                  case "left":
9211                      if(w - l > cw){
9212                          var v = Math.min(l + distance, w-cw);
9213                          this.scrollTo("left", v, a);
9214                          scrolled = true;
9215                      }
9216                      break;
9217                 case "r":
9218                 case "right":
9219                      if(l > 0){
9220                          var v = Math.max(l - distance, 0);
9221                          this.scrollTo("left", v, a);
9222                          scrolled = true;
9223                      }
9224                      break;
9225                 case "t":
9226                 case "top":
9227                 case "up":
9228                      if(t > 0){
9229                          var v = Math.max(t - distance, 0);
9230                          this.scrollTo("top", v, a);
9231                          scrolled = true;
9232                      }
9233                      break;
9234                 case "b":
9235                 case "bottom":
9236                 case "down":
9237                      if(h - t > ch){
9238                          var v = Math.min(t + distance, h-ch);
9239                          this.scrollTo("top", v, a);
9240                          scrolled = true;
9241                      }
9242                      break;
9243              }
9244              return scrolled;
9245         },
9246
9247         /**
9248          * Translates the passed page coordinates into left/top css values for this element
9249          * @param {Number/Array} x The page x or an array containing [x, y]
9250          * @param {Number} y The page y
9251          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9252          */
9253         translatePoints : function(x, y){
9254             if(typeof x == 'object' || x instanceof Array){
9255                 y = x[1]; x = x[0];
9256             }
9257             var p = this.getStyle('position');
9258             var o = this.getXY();
9259
9260             var l = parseInt(this.getStyle('left'), 10);
9261             var t = parseInt(this.getStyle('top'), 10);
9262
9263             if(isNaN(l)){
9264                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9265             }
9266             if(isNaN(t)){
9267                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9268             }
9269
9270             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9271         },
9272
9273         /**
9274          * Returns the current scroll position of the element.
9275          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9276          */
9277         getScroll : function(){
9278             var d = this.dom, doc = document;
9279             if(d == doc || d == doc.body){
9280                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9281                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9282                 return {left: l, top: t};
9283             }else{
9284                 return {left: d.scrollLeft, top: d.scrollTop};
9285             }
9286         },
9287
9288         /**
9289          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9290          * are convert to standard 6 digit hex color.
9291          * @param {String} attr The css attribute
9292          * @param {String} defaultValue The default value to use when a valid color isn't found
9293          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9294          * YUI color anims.
9295          */
9296         getColor : function(attr, defaultValue, prefix){
9297             var v = this.getStyle(attr);
9298             if(!v || v == "transparent" || v == "inherit") {
9299                 return defaultValue;
9300             }
9301             var color = typeof prefix == "undefined" ? "#" : prefix;
9302             if(v.substr(0, 4) == "rgb("){
9303                 var rvs = v.slice(4, v.length -1).split(",");
9304                 for(var i = 0; i < 3; i++){
9305                     var h = parseInt(rvs[i]).toString(16);
9306                     if(h < 16){
9307                         h = "0" + h;
9308                     }
9309                     color += h;
9310                 }
9311             } else {
9312                 if(v.substr(0, 1) == "#"){
9313                     if(v.length == 4) {
9314                         for(var i = 1; i < 4; i++){
9315                             var c = v.charAt(i);
9316                             color +=  c + c;
9317                         }
9318                     }else if(v.length == 7){
9319                         color += v.substr(1);
9320                     }
9321                 }
9322             }
9323             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9324         },
9325
9326         /**
9327          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9328          * gradient background, rounded corners and a 4-way shadow.
9329          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9330          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9331          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9332          * @return {Roo.Element} this
9333          */
9334         boxWrap : function(cls){
9335             cls = cls || 'x-box';
9336             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9337             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9338             return el;
9339         },
9340
9341         /**
9342          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9343          * @param {String} namespace The namespace in which to look for the attribute
9344          * @param {String} name The attribute name
9345          * @return {String} The attribute value
9346          */
9347         getAttributeNS : Roo.isIE ? function(ns, name){
9348             var d = this.dom;
9349             var type = typeof d[ns+":"+name];
9350             if(type != 'undefined' && type != 'unknown'){
9351                 return d[ns+":"+name];
9352             }
9353             return d[name];
9354         } : function(ns, name){
9355             var d = this.dom;
9356             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9357         }
9358     };
9359
9360     var ep = El.prototype;
9361
9362     /**
9363      * Appends an event handler (Shorthand for addListener)
9364      * @param {String}   eventName     The type of event to append
9365      * @param {Function} fn        The method the event invokes
9366      * @param {Object} scope       (optional) The scope (this object) of the fn
9367      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9368      * @method
9369      */
9370     ep.on = ep.addListener;
9371         // backwards compat
9372     ep.mon = ep.addListener;
9373
9374     /**
9375      * Removes an event handler from this element (shorthand for removeListener)
9376      * @param {String} eventName the type of event to remove
9377      * @param {Function} fn the method the event invokes
9378      * @return {Roo.Element} this
9379      * @method
9380      */
9381     ep.un = ep.removeListener;
9382
9383     /**
9384      * true to automatically adjust width and height settings for box-model issues (default to true)
9385      */
9386     ep.autoBoxAdjust = true;
9387
9388     // private
9389     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9390
9391     // private
9392     El.addUnits = function(v, defaultUnit){
9393         if(v === "" || v == "auto"){
9394             return v;
9395         }
9396         if(v === undefined){
9397             return '';
9398         }
9399         if(typeof v == "number" || !El.unitPattern.test(v)){
9400             return v + (defaultUnit || 'px');
9401         }
9402         return v;
9403     };
9404
9405     // special markup used throughout Roo when box wrapping elements
9406     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9407     /**
9408      * Visibility mode constant - Use visibility to hide element
9409      * @static
9410      * @type Number
9411      */
9412     El.VISIBILITY = 1;
9413     /**
9414      * Visibility mode constant - Use display to hide element
9415      * @static
9416      * @type Number
9417      */
9418     El.DISPLAY = 2;
9419
9420     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9421     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9422     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9423
9424
9425
9426     /**
9427      * @private
9428      */
9429     El.cache = {};
9430
9431     var docEl;
9432
9433     /**
9434      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9435      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9436      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9437      * @return {Element} The Element object
9438      * @static
9439      */
9440     El.get = function(el){
9441         var ex, elm, id;
9442         if(!el){ return null; }
9443         if(typeof el == "string"){ // element id
9444             if(!(elm = document.getElementById(el))){
9445                 return null;
9446             }
9447             if(ex = El.cache[el]){
9448                 ex.dom = elm;
9449             }else{
9450                 ex = El.cache[el] = new El(elm);
9451             }
9452             return ex;
9453         }else if(el.tagName){ // dom element
9454             if(!(id = el.id)){
9455                 id = Roo.id(el);
9456             }
9457             if(ex = El.cache[id]){
9458                 ex.dom = el;
9459             }else{
9460                 ex = El.cache[id] = new El(el);
9461             }
9462             return ex;
9463         }else if(el instanceof El){
9464             if(el != docEl){
9465                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9466                                                               // catch case where it hasn't been appended
9467                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9468             }
9469             return el;
9470         }else if(el.isComposite){
9471             return el;
9472         }else if(el instanceof Array){
9473             return El.select(el);
9474         }else if(el == document){
9475             // create a bogus element object representing the document object
9476             if(!docEl){
9477                 var f = function(){};
9478                 f.prototype = El.prototype;
9479                 docEl = new f();
9480                 docEl.dom = document;
9481             }
9482             return docEl;
9483         }
9484         return null;
9485     };
9486
9487     // private
9488     El.uncache = function(el){
9489         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9490             if(a[i]){
9491                 delete El.cache[a[i].id || a[i]];
9492             }
9493         }
9494     };
9495
9496     // private
9497     // Garbage collection - uncache elements/purge listeners on orphaned elements
9498     // so we don't hold a reference and cause the browser to retain them
9499     El.garbageCollect = function(){
9500         if(!Roo.enableGarbageCollector){
9501             clearInterval(El.collectorThread);
9502             return;
9503         }
9504         for(var eid in El.cache){
9505             var el = El.cache[eid], d = el.dom;
9506             // -------------------------------------------------------
9507             // Determining what is garbage:
9508             // -------------------------------------------------------
9509             // !d
9510             // dom node is null, definitely garbage
9511             // -------------------------------------------------------
9512             // !d.parentNode
9513             // no parentNode == direct orphan, definitely garbage
9514             // -------------------------------------------------------
9515             // !d.offsetParent && !document.getElementById(eid)
9516             // display none elements have no offsetParent so we will
9517             // also try to look it up by it's id. However, check
9518             // offsetParent first so we don't do unneeded lookups.
9519             // This enables collection of elements that are not orphans
9520             // directly, but somewhere up the line they have an orphan
9521             // parent.
9522             // -------------------------------------------------------
9523             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9524                 delete El.cache[eid];
9525                 if(d && Roo.enableListenerCollection){
9526                     E.purgeElement(d);
9527                 }
9528             }
9529         }
9530     }
9531     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9532
9533
9534     // dom is optional
9535     El.Flyweight = function(dom){
9536         this.dom = dom;
9537     };
9538     El.Flyweight.prototype = El.prototype;
9539
9540     El._flyweights = {};
9541     /**
9542      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9543      * the dom node can be overwritten by other code.
9544      * @param {String/HTMLElement} el The dom node or id
9545      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9546      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9547      * @static
9548      * @return {Element} The shared Element object
9549      */
9550     El.fly = function(el, named){
9551         named = named || '_global';
9552         el = Roo.getDom(el);
9553         if(!el){
9554             return null;
9555         }
9556         if(!El._flyweights[named]){
9557             El._flyweights[named] = new El.Flyweight();
9558         }
9559         El._flyweights[named].dom = el;
9560         return El._flyweights[named];
9561     };
9562
9563     /**
9564      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9565      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9566      * Shorthand of {@link Roo.Element#get}
9567      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9568      * @return {Element} The Element object
9569      * @member Roo
9570      * @method get
9571      */
9572     Roo.get = El.get;
9573     /**
9574      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9575      * the dom node can be overwritten by other code.
9576      * Shorthand of {@link Roo.Element#fly}
9577      * @param {String/HTMLElement} el The dom node or id
9578      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9579      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9580      * @static
9581      * @return {Element} The shared Element object
9582      * @member Roo
9583      * @method fly
9584      */
9585     Roo.fly = El.fly;
9586
9587     // speedy lookup for elements never to box adjust
9588     var noBoxAdjust = Roo.isStrict ? {
9589         select:1
9590     } : {
9591         input:1, select:1, textarea:1
9592     };
9593     if(Roo.isIE || Roo.isGecko){
9594         noBoxAdjust['button'] = 1;
9595     }
9596
9597
9598     Roo.EventManager.on(window, 'unload', function(){
9599         delete El.cache;
9600         delete El._flyweights;
9601     });
9602 })();
9603
9604
9605
9606
9607 if(Roo.DomQuery){
9608     Roo.Element.selectorFunction = Roo.DomQuery.select;
9609 }
9610
9611 Roo.Element.select = function(selector, unique, root){
9612     var els;
9613     if(typeof selector == "string"){
9614         els = Roo.Element.selectorFunction(selector, root);
9615     }else if(selector.length !== undefined){
9616         els = selector;
9617     }else{
9618         throw "Invalid selector";
9619     }
9620     if(unique === true){
9621         return new Roo.CompositeElement(els);
9622     }else{
9623         return new Roo.CompositeElementLite(els);
9624     }
9625 };
9626 /**
9627  * Selects elements based on the passed CSS selector to enable working on them as 1.
9628  * @param {String/Array} selector The CSS selector or an array of elements
9629  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9630  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9631  * @return {CompositeElementLite/CompositeElement}
9632  * @member Roo
9633  * @method select
9634  */
9635 Roo.select = Roo.Element.select;
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645
9646
9647
9648
9649
9650 /*
9651  * Based on:
9652  * Ext JS Library 1.1.1
9653  * Copyright(c) 2006-2007, Ext JS, LLC.
9654  *
9655  * Originally Released Under LGPL - original licence link has changed is not relivant.
9656  *
9657  * Fork - LGPL
9658  * <script type="text/javascript">
9659  */
9660
9661
9662
9663 //Notifies Element that fx methods are available
9664 Roo.enableFx = true;
9665
9666 /**
9667  * @class Roo.Fx
9668  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9669  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9670  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9671  * Element effects to work.</p><br/>
9672  *
9673  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9674  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9675  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9676  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9677  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9678  * expected results and should be done with care.</p><br/>
9679  *
9680  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9681  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9682 <pre>
9683 Value  Description
9684 -----  -----------------------------
9685 tl     The top left corner
9686 t      The center of the top edge
9687 tr     The top right corner
9688 l      The center of the left edge
9689 r      The center of the right edge
9690 bl     The bottom left corner
9691 b      The center of the bottom edge
9692 br     The bottom right corner
9693 </pre>
9694  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9695  * below are common options that can be passed to any Fx method.</b>
9696  * @cfg {Function} callback A function called when the effect is finished
9697  * @cfg {Object} scope The scope of the effect function
9698  * @cfg {String} easing A valid Easing value for the effect
9699  * @cfg {String} afterCls A css class to apply after the effect
9700  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9701  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9702  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9703  * effects that end with the element being visually hidden, ignored otherwise)
9704  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9705  * a function which returns such a specification that will be applied to the Element after the effect finishes
9706  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9707  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9708  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9709  */
9710 Roo.Fx = {
9711         /**
9712          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9713          * origin for the slide effect.  This function automatically handles wrapping the element with
9714          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9715          * Usage:
9716          *<pre><code>
9717 // default: slide the element in from the top
9718 el.slideIn();
9719
9720 // custom: slide the element in from the right with a 2-second duration
9721 el.slideIn('r', { duration: 2 });
9722
9723 // common config options shown with default values
9724 el.slideIn('t', {
9725     easing: 'easeOut',
9726     duration: .5
9727 });
9728 </code></pre>
9729          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9730          * @param {Object} options (optional) Object literal with any of the Fx config options
9731          * @return {Roo.Element} The Element
9732          */
9733     slideIn : function(anchor, o){
9734         var el = this.getFxEl();
9735         o = o || {};
9736
9737         el.queueFx(o, function(){
9738
9739             anchor = anchor || "t";
9740
9741             // fix display to visibility
9742             this.fixDisplay();
9743
9744             // restore values after effect
9745             var r = this.getFxRestore();
9746             var b = this.getBox();
9747             // fixed size for slide
9748             this.setSize(b);
9749
9750             // wrap if needed
9751             var wrap = this.fxWrap(r.pos, o, "hidden");
9752
9753             var st = this.dom.style;
9754             st.visibility = "visible";
9755             st.position = "absolute";
9756
9757             // clear out temp styles after slide and unwrap
9758             var after = function(){
9759                 el.fxUnwrap(wrap, r.pos, o);
9760                 st.width = r.width;
9761                 st.height = r.height;
9762                 el.afterFx(o);
9763             };
9764             // time to calc the positions
9765             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9766
9767             switch(anchor.toLowerCase()){
9768                 case "t":
9769                     wrap.setSize(b.width, 0);
9770                     st.left = st.bottom = "0";
9771                     a = {height: bh};
9772                 break;
9773                 case "l":
9774                     wrap.setSize(0, b.height);
9775                     st.right = st.top = "0";
9776                     a = {width: bw};
9777                 break;
9778                 case "r":
9779                     wrap.setSize(0, b.height);
9780                     wrap.setX(b.right);
9781                     st.left = st.top = "0";
9782                     a = {width: bw, points: pt};
9783                 break;
9784                 case "b":
9785                     wrap.setSize(b.width, 0);
9786                     wrap.setY(b.bottom);
9787                     st.left = st.top = "0";
9788                     a = {height: bh, points: pt};
9789                 break;
9790                 case "tl":
9791                     wrap.setSize(0, 0);
9792                     st.right = st.bottom = "0";
9793                     a = {width: bw, height: bh};
9794                 break;
9795                 case "bl":
9796                     wrap.setSize(0, 0);
9797                     wrap.setY(b.y+b.height);
9798                     st.right = st.top = "0";
9799                     a = {width: bw, height: bh, points: pt};
9800                 break;
9801                 case "br":
9802                     wrap.setSize(0, 0);
9803                     wrap.setXY([b.right, b.bottom]);
9804                     st.left = st.top = "0";
9805                     a = {width: bw, height: bh, points: pt};
9806                 break;
9807                 case "tr":
9808                     wrap.setSize(0, 0);
9809                     wrap.setX(b.x+b.width);
9810                     st.left = st.bottom = "0";
9811                     a = {width: bw, height: bh, points: pt};
9812                 break;
9813             }
9814             this.dom.style.visibility = "visible";
9815             wrap.show();
9816
9817             arguments.callee.anim = wrap.fxanim(a,
9818                 o,
9819                 'motion',
9820                 .5,
9821                 'easeOut', after);
9822         });
9823         return this;
9824     },
9825     
9826         /**
9827          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9828          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9829          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9830          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9831          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9832          * Usage:
9833          *<pre><code>
9834 // default: slide the element out to the top
9835 el.slideOut();
9836
9837 // custom: slide the element out to the right with a 2-second duration
9838 el.slideOut('r', { duration: 2 });
9839
9840 // common config options shown with default values
9841 el.slideOut('t', {
9842     easing: 'easeOut',
9843     duration: .5,
9844     remove: false,
9845     useDisplay: false
9846 });
9847 </code></pre>
9848          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9849          * @param {Object} options (optional) Object literal with any of the Fx config options
9850          * @return {Roo.Element} The Element
9851          */
9852     slideOut : function(anchor, o){
9853         var el = this.getFxEl();
9854         o = o || {};
9855
9856         el.queueFx(o, function(){
9857
9858             anchor = anchor || "t";
9859
9860             // restore values after effect
9861             var r = this.getFxRestore();
9862             
9863             var b = this.getBox();
9864             // fixed size for slide
9865             this.setSize(b);
9866
9867             // wrap if needed
9868             var wrap = this.fxWrap(r.pos, o, "visible");
9869
9870             var st = this.dom.style;
9871             st.visibility = "visible";
9872             st.position = "absolute";
9873
9874             wrap.setSize(b);
9875
9876             var after = function(){
9877                 if(o.useDisplay){
9878                     el.setDisplayed(false);
9879                 }else{
9880                     el.hide();
9881                 }
9882
9883                 el.fxUnwrap(wrap, r.pos, o);
9884
9885                 st.width = r.width;
9886                 st.height = r.height;
9887
9888                 el.afterFx(o);
9889             };
9890
9891             var a, zero = {to: 0};
9892             switch(anchor.toLowerCase()){
9893                 case "t":
9894                     st.left = st.bottom = "0";
9895                     a = {height: zero};
9896                 break;
9897                 case "l":
9898                     st.right = st.top = "0";
9899                     a = {width: zero};
9900                 break;
9901                 case "r":
9902                     st.left = st.top = "0";
9903                     a = {width: zero, points: {to:[b.right, b.y]}};
9904                 break;
9905                 case "b":
9906                     st.left = st.top = "0";
9907                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9908                 break;
9909                 case "tl":
9910                     st.right = st.bottom = "0";
9911                     a = {width: zero, height: zero};
9912                 break;
9913                 case "bl":
9914                     st.right = st.top = "0";
9915                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9916                 break;
9917                 case "br":
9918                     st.left = st.top = "0";
9919                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9920                 break;
9921                 case "tr":
9922                     st.left = st.bottom = "0";
9923                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9924                 break;
9925             }
9926
9927             arguments.callee.anim = wrap.fxanim(a,
9928                 o,
9929                 'motion',
9930                 .5,
9931                 "easeOut", after);
9932         });
9933         return this;
9934     },
9935
9936         /**
9937          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9938          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9939          * The element must be removed from the DOM using the 'remove' config option if desired.
9940          * Usage:
9941          *<pre><code>
9942 // default
9943 el.puff();
9944
9945 // common config options shown with default values
9946 el.puff({
9947     easing: 'easeOut',
9948     duration: .5,
9949     remove: false,
9950     useDisplay: false
9951 });
9952 </code></pre>
9953          * @param {Object} options (optional) Object literal with any of the Fx config options
9954          * @return {Roo.Element} The Element
9955          */
9956     puff : function(o){
9957         var el = this.getFxEl();
9958         o = o || {};
9959
9960         el.queueFx(o, function(){
9961             this.clearOpacity();
9962             this.show();
9963
9964             // restore values after effect
9965             var r = this.getFxRestore();
9966             var st = this.dom.style;
9967
9968             var after = function(){
9969                 if(o.useDisplay){
9970                     el.setDisplayed(false);
9971                 }else{
9972                     el.hide();
9973                 }
9974
9975                 el.clearOpacity();
9976
9977                 el.setPositioning(r.pos);
9978                 st.width = r.width;
9979                 st.height = r.height;
9980                 st.fontSize = '';
9981                 el.afterFx(o);
9982             };
9983
9984             var width = this.getWidth();
9985             var height = this.getHeight();
9986
9987             arguments.callee.anim = this.fxanim({
9988                     width : {to: this.adjustWidth(width * 2)},
9989                     height : {to: this.adjustHeight(height * 2)},
9990                     points : {by: [-(width * .5), -(height * .5)]},
9991                     opacity : {to: 0},
9992                     fontSize: {to:200, unit: "%"}
9993                 },
9994                 o,
9995                 'motion',
9996                 .5,
9997                 "easeOut", after);
9998         });
9999         return this;
10000     },
10001
10002         /**
10003          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10004          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10005          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10006          * Usage:
10007          *<pre><code>
10008 // default
10009 el.switchOff();
10010
10011 // all config options shown with default values
10012 el.switchOff({
10013     easing: 'easeIn',
10014     duration: .3,
10015     remove: false,
10016     useDisplay: false
10017 });
10018 </code></pre>
10019          * @param {Object} options (optional) Object literal with any of the Fx config options
10020          * @return {Roo.Element} The Element
10021          */
10022     switchOff : function(o){
10023         var el = this.getFxEl();
10024         o = o || {};
10025
10026         el.queueFx(o, function(){
10027             this.clearOpacity();
10028             this.clip();
10029
10030             // restore values after effect
10031             var r = this.getFxRestore();
10032             var st = this.dom.style;
10033
10034             var after = function(){
10035                 if(o.useDisplay){
10036                     el.setDisplayed(false);
10037                 }else{
10038                     el.hide();
10039                 }
10040
10041                 el.clearOpacity();
10042                 el.setPositioning(r.pos);
10043                 st.width = r.width;
10044                 st.height = r.height;
10045
10046                 el.afterFx(o);
10047             };
10048
10049             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10050                 this.clearOpacity();
10051                 (function(){
10052                     this.fxanim({
10053                         height:{to:1},
10054                         points:{by:[0, this.getHeight() * .5]}
10055                     }, o, 'motion', 0.3, 'easeIn', after);
10056                 }).defer(100, this);
10057             });
10058         });
10059         return this;
10060     },
10061
10062     /**
10063      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10064      * changed using the "attr" config option) and then fading back to the original color. If no original
10065      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10066      * Usage:
10067 <pre><code>
10068 // default: highlight background to yellow
10069 el.highlight();
10070
10071 // custom: highlight foreground text to blue for 2 seconds
10072 el.highlight("0000ff", { attr: 'color', duration: 2 });
10073
10074 // common config options shown with default values
10075 el.highlight("ffff9c", {
10076     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10077     endColor: (current color) or "ffffff",
10078     easing: 'easeIn',
10079     duration: 1
10080 });
10081 </code></pre>
10082      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10083      * @param {Object} options (optional) Object literal with any of the Fx config options
10084      * @return {Roo.Element} The Element
10085      */ 
10086     highlight : function(color, o){
10087         var el = this.getFxEl();
10088         o = o || {};
10089
10090         el.queueFx(o, function(){
10091             color = color || "ffff9c";
10092             attr = o.attr || "backgroundColor";
10093
10094             this.clearOpacity();
10095             this.show();
10096
10097             var origColor = this.getColor(attr);
10098             var restoreColor = this.dom.style[attr];
10099             endColor = (o.endColor || origColor) || "ffffff";
10100
10101             var after = function(){
10102                 el.dom.style[attr] = restoreColor;
10103                 el.afterFx(o);
10104             };
10105
10106             var a = {};
10107             a[attr] = {from: color, to: endColor};
10108             arguments.callee.anim = this.fxanim(a,
10109                 o,
10110                 'color',
10111                 1,
10112                 'easeIn', after);
10113         });
10114         return this;
10115     },
10116
10117    /**
10118     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10119     * Usage:
10120 <pre><code>
10121 // default: a single light blue ripple
10122 el.frame();
10123
10124 // custom: 3 red ripples lasting 3 seconds total
10125 el.frame("ff0000", 3, { duration: 3 });
10126
10127 // common config options shown with default values
10128 el.frame("C3DAF9", 1, {
10129     duration: 1 //duration of entire animation (not each individual ripple)
10130     // Note: Easing is not configurable and will be ignored if included
10131 });
10132 </code></pre>
10133     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10134     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10135     * @param {Object} options (optional) Object literal with any of the Fx config options
10136     * @return {Roo.Element} The Element
10137     */
10138     frame : function(color, count, o){
10139         var el = this.getFxEl();
10140         o = o || {};
10141
10142         el.queueFx(o, function(){
10143             color = color || "#C3DAF9";
10144             if(color.length == 6){
10145                 color = "#" + color;
10146             }
10147             count = count || 1;
10148             duration = o.duration || 1;
10149             this.show();
10150
10151             var b = this.getBox();
10152             var animFn = function(){
10153                 var proxy = this.createProxy({
10154
10155                      style:{
10156                         visbility:"hidden",
10157                         position:"absolute",
10158                         "z-index":"35000", // yee haw
10159                         border:"0px solid " + color
10160                      }
10161                   });
10162                 var scale = Roo.isBorderBox ? 2 : 1;
10163                 proxy.animate({
10164                     top:{from:b.y, to:b.y - 20},
10165                     left:{from:b.x, to:b.x - 20},
10166                     borderWidth:{from:0, to:10},
10167                     opacity:{from:1, to:0},
10168                     height:{from:b.height, to:(b.height + (20*scale))},
10169                     width:{from:b.width, to:(b.width + (20*scale))}
10170                 }, duration, function(){
10171                     proxy.remove();
10172                 });
10173                 if(--count > 0){
10174                      animFn.defer((duration/2)*1000, this);
10175                 }else{
10176                     el.afterFx(o);
10177                 }
10178             };
10179             animFn.call(this);
10180         });
10181         return this;
10182     },
10183
10184    /**
10185     * Creates a pause before any subsequent queued effects begin.  If there are
10186     * no effects queued after the pause it will have no effect.
10187     * Usage:
10188 <pre><code>
10189 el.pause(1);
10190 </code></pre>
10191     * @param {Number} seconds The length of time to pause (in seconds)
10192     * @return {Roo.Element} The Element
10193     */
10194     pause : function(seconds){
10195         var el = this.getFxEl();
10196         var o = {};
10197
10198         el.queueFx(o, function(){
10199             setTimeout(function(){
10200                 el.afterFx(o);
10201             }, seconds * 1000);
10202         });
10203         return this;
10204     },
10205
10206    /**
10207     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10208     * using the "endOpacity" config option.
10209     * Usage:
10210 <pre><code>
10211 // default: fade in from opacity 0 to 100%
10212 el.fadeIn();
10213
10214 // custom: fade in from opacity 0 to 75% over 2 seconds
10215 el.fadeIn({ endOpacity: .75, duration: 2});
10216
10217 // common config options shown with default values
10218 el.fadeIn({
10219     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10220     easing: 'easeOut',
10221     duration: .5
10222 });
10223 </code></pre>
10224     * @param {Object} options (optional) Object literal with any of the Fx config options
10225     * @return {Roo.Element} The Element
10226     */
10227     fadeIn : function(o){
10228         var el = this.getFxEl();
10229         o = o || {};
10230         el.queueFx(o, function(){
10231             this.setOpacity(0);
10232             this.fixDisplay();
10233             this.dom.style.visibility = 'visible';
10234             var to = o.endOpacity || 1;
10235             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10236                 o, null, .5, "easeOut", function(){
10237                 if(to == 1){
10238                     this.clearOpacity();
10239                 }
10240                 el.afterFx(o);
10241             });
10242         });
10243         return this;
10244     },
10245
10246    /**
10247     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10248     * using the "endOpacity" config option.
10249     * Usage:
10250 <pre><code>
10251 // default: fade out from the element's current opacity to 0
10252 el.fadeOut();
10253
10254 // custom: fade out from the element's current opacity to 25% over 2 seconds
10255 el.fadeOut({ endOpacity: .25, duration: 2});
10256
10257 // common config options shown with default values
10258 el.fadeOut({
10259     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10260     easing: 'easeOut',
10261     duration: .5
10262     remove: false,
10263     useDisplay: false
10264 });
10265 </code></pre>
10266     * @param {Object} options (optional) Object literal with any of the Fx config options
10267     * @return {Roo.Element} The Element
10268     */
10269     fadeOut : function(o){
10270         var el = this.getFxEl();
10271         o = o || {};
10272         el.queueFx(o, function(){
10273             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10274                 o, null, .5, "easeOut", function(){
10275                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10276                      this.dom.style.display = "none";
10277                 }else{
10278                      this.dom.style.visibility = "hidden";
10279                 }
10280                 this.clearOpacity();
10281                 el.afterFx(o);
10282             });
10283         });
10284         return this;
10285     },
10286
10287    /**
10288     * Animates the transition of an element's dimensions from a starting height/width
10289     * to an ending height/width.
10290     * Usage:
10291 <pre><code>
10292 // change height and width to 100x100 pixels
10293 el.scale(100, 100);
10294
10295 // common config options shown with default values.  The height and width will default to
10296 // the element's existing values if passed as null.
10297 el.scale(
10298     [element's width],
10299     [element's height], {
10300     easing: 'easeOut',
10301     duration: .35
10302 });
10303 </code></pre>
10304     * @param {Number} width  The new width (pass undefined to keep the original width)
10305     * @param {Number} height  The new height (pass undefined to keep the original height)
10306     * @param {Object} options (optional) Object literal with any of the Fx config options
10307     * @return {Roo.Element} The Element
10308     */
10309     scale : function(w, h, o){
10310         this.shift(Roo.apply({}, o, {
10311             width: w,
10312             height: h
10313         }));
10314         return this;
10315     },
10316
10317    /**
10318     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10319     * Any of these properties not specified in the config object will not be changed.  This effect 
10320     * requires that at least one new dimension, position or opacity setting must be passed in on
10321     * the config object in order for the function to have any effect.
10322     * Usage:
10323 <pre><code>
10324 // slide the element horizontally to x position 200 while changing the height and opacity
10325 el.shift({ x: 200, height: 50, opacity: .8 });
10326
10327 // common config options shown with default values.
10328 el.shift({
10329     width: [element's width],
10330     height: [element's height],
10331     x: [element's x position],
10332     y: [element's y position],
10333     opacity: [element's opacity],
10334     easing: 'easeOut',
10335     duration: .35
10336 });
10337 </code></pre>
10338     * @param {Object} options  Object literal with any of the Fx config options
10339     * @return {Roo.Element} The Element
10340     */
10341     shift : function(o){
10342         var el = this.getFxEl();
10343         o = o || {};
10344         el.queueFx(o, function(){
10345             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10346             if(w !== undefined){
10347                 a.width = {to: this.adjustWidth(w)};
10348             }
10349             if(h !== undefined){
10350                 a.height = {to: this.adjustHeight(h)};
10351             }
10352             if(x !== undefined || y !== undefined){
10353                 a.points = {to: [
10354                     x !== undefined ? x : this.getX(),
10355                     y !== undefined ? y : this.getY()
10356                 ]};
10357             }
10358             if(op !== undefined){
10359                 a.opacity = {to: op};
10360             }
10361             if(o.xy !== undefined){
10362                 a.points = {to: o.xy};
10363             }
10364             arguments.callee.anim = this.fxanim(a,
10365                 o, 'motion', .35, "easeOut", function(){
10366                 el.afterFx(o);
10367             });
10368         });
10369         return this;
10370     },
10371
10372         /**
10373          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10374          * ending point of the effect.
10375          * Usage:
10376          *<pre><code>
10377 // default: slide the element downward while fading out
10378 el.ghost();
10379
10380 // custom: slide the element out to the right with a 2-second duration
10381 el.ghost('r', { duration: 2 });
10382
10383 // common config options shown with default values
10384 el.ghost('b', {
10385     easing: 'easeOut',
10386     duration: .5
10387     remove: false,
10388     useDisplay: false
10389 });
10390 </code></pre>
10391          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10392          * @param {Object} options (optional) Object literal with any of the Fx config options
10393          * @return {Roo.Element} The Element
10394          */
10395     ghost : function(anchor, o){
10396         var el = this.getFxEl();
10397         o = o || {};
10398
10399         el.queueFx(o, function(){
10400             anchor = anchor || "b";
10401
10402             // restore values after effect
10403             var r = this.getFxRestore();
10404             var w = this.getWidth(),
10405                 h = this.getHeight();
10406
10407             var st = this.dom.style;
10408
10409             var after = function(){
10410                 if(o.useDisplay){
10411                     el.setDisplayed(false);
10412                 }else{
10413                     el.hide();
10414                 }
10415
10416                 el.clearOpacity();
10417                 el.setPositioning(r.pos);
10418                 st.width = r.width;
10419                 st.height = r.height;
10420
10421                 el.afterFx(o);
10422             };
10423
10424             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10425             switch(anchor.toLowerCase()){
10426                 case "t":
10427                     pt.by = [0, -h];
10428                 break;
10429                 case "l":
10430                     pt.by = [-w, 0];
10431                 break;
10432                 case "r":
10433                     pt.by = [w, 0];
10434                 break;
10435                 case "b":
10436                     pt.by = [0, h];
10437                 break;
10438                 case "tl":
10439                     pt.by = [-w, -h];
10440                 break;
10441                 case "bl":
10442                     pt.by = [-w, h];
10443                 break;
10444                 case "br":
10445                     pt.by = [w, h];
10446                 break;
10447                 case "tr":
10448                     pt.by = [w, -h];
10449                 break;
10450             }
10451
10452             arguments.callee.anim = this.fxanim(a,
10453                 o,
10454                 'motion',
10455                 .5,
10456                 "easeOut", after);
10457         });
10458         return this;
10459     },
10460
10461         /**
10462          * Ensures that all effects queued after syncFx is called on the element are
10463          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10464          * @return {Roo.Element} The Element
10465          */
10466     syncFx : function(){
10467         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10468             block : false,
10469             concurrent : true,
10470             stopFx : false
10471         });
10472         return this;
10473     },
10474
10475         /**
10476          * Ensures that all effects queued after sequenceFx is called on the element are
10477          * run in sequence.  This is the opposite of {@link #syncFx}.
10478          * @return {Roo.Element} The Element
10479          */
10480     sequenceFx : function(){
10481         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10482             block : false,
10483             concurrent : false,
10484             stopFx : false
10485         });
10486         return this;
10487     },
10488
10489         /* @private */
10490     nextFx : function(){
10491         var ef = this.fxQueue[0];
10492         if(ef){
10493             ef.call(this);
10494         }
10495     },
10496
10497         /**
10498          * Returns true if the element has any effects actively running or queued, else returns false.
10499          * @return {Boolean} True if element has active effects, else false
10500          */
10501     hasActiveFx : function(){
10502         return this.fxQueue && this.fxQueue[0];
10503     },
10504
10505         /**
10506          * Stops any running effects and clears the element's internal effects queue if it contains
10507          * any additional effects that haven't started yet.
10508          * @return {Roo.Element} The Element
10509          */
10510     stopFx : function(){
10511         if(this.hasActiveFx()){
10512             var cur = this.fxQueue[0];
10513             if(cur && cur.anim && cur.anim.isAnimated()){
10514                 this.fxQueue = [cur]; // clear out others
10515                 cur.anim.stop(true);
10516             }
10517         }
10518         return this;
10519     },
10520
10521         /* @private */
10522     beforeFx : function(o){
10523         if(this.hasActiveFx() && !o.concurrent){
10524            if(o.stopFx){
10525                this.stopFx();
10526                return true;
10527            }
10528            return false;
10529         }
10530         return true;
10531     },
10532
10533         /**
10534          * Returns true if the element is currently blocking so that no other effect can be queued
10535          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10536          * used to ensure that an effect initiated by a user action runs to completion prior to the
10537          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10538          * @return {Boolean} True if blocking, else false
10539          */
10540     hasFxBlock : function(){
10541         var q = this.fxQueue;
10542         return q && q[0] && q[0].block;
10543     },
10544
10545         /* @private */
10546     queueFx : function(o, fn){
10547         if(!this.fxQueue){
10548             this.fxQueue = [];
10549         }
10550         if(!this.hasFxBlock()){
10551             Roo.applyIf(o, this.fxDefaults);
10552             if(!o.concurrent){
10553                 var run = this.beforeFx(o);
10554                 fn.block = o.block;
10555                 this.fxQueue.push(fn);
10556                 if(run){
10557                     this.nextFx();
10558                 }
10559             }else{
10560                 fn.call(this);
10561             }
10562         }
10563         return this;
10564     },
10565
10566         /* @private */
10567     fxWrap : function(pos, o, vis){
10568         var wrap;
10569         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10570             var wrapXY;
10571             if(o.fixPosition){
10572                 wrapXY = this.getXY();
10573             }
10574             var div = document.createElement("div");
10575             div.style.visibility = vis;
10576             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10577             wrap.setPositioning(pos);
10578             if(wrap.getStyle("position") == "static"){
10579                 wrap.position("relative");
10580             }
10581             this.clearPositioning('auto');
10582             wrap.clip();
10583             wrap.dom.appendChild(this.dom);
10584             if(wrapXY){
10585                 wrap.setXY(wrapXY);
10586             }
10587         }
10588         return wrap;
10589     },
10590
10591         /* @private */
10592     fxUnwrap : function(wrap, pos, o){
10593         this.clearPositioning();
10594         this.setPositioning(pos);
10595         if(!o.wrap){
10596             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10597             wrap.remove();
10598         }
10599     },
10600
10601         /* @private */
10602     getFxRestore : function(){
10603         var st = this.dom.style;
10604         return {pos: this.getPositioning(), width: st.width, height : st.height};
10605     },
10606
10607         /* @private */
10608     afterFx : function(o){
10609         if(o.afterStyle){
10610             this.applyStyles(o.afterStyle);
10611         }
10612         if(o.afterCls){
10613             this.addClass(o.afterCls);
10614         }
10615         if(o.remove === true){
10616             this.remove();
10617         }
10618         Roo.callback(o.callback, o.scope, [this]);
10619         if(!o.concurrent){
10620             this.fxQueue.shift();
10621             this.nextFx();
10622         }
10623     },
10624
10625         /* @private */
10626     getFxEl : function(){ // support for composite element fx
10627         return Roo.get(this.dom);
10628     },
10629
10630         /* @private */
10631     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10632         animType = animType || 'run';
10633         opt = opt || {};
10634         var anim = Roo.lib.Anim[animType](
10635             this.dom, args,
10636             (opt.duration || defaultDur) || .35,
10637             (opt.easing || defaultEase) || 'easeOut',
10638             function(){
10639                 Roo.callback(cb, this);
10640             },
10641             this
10642         );
10643         opt.anim = anim;
10644         return anim;
10645     }
10646 };
10647
10648 // backwords compat
10649 Roo.Fx.resize = Roo.Fx.scale;
10650
10651 //When included, Roo.Fx is automatically applied to Element so that all basic
10652 //effects are available directly via the Element API
10653 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10654  * Based on:
10655  * Ext JS Library 1.1.1
10656  * Copyright(c) 2006-2007, Ext JS, LLC.
10657  *
10658  * Originally Released Under LGPL - original licence link has changed is not relivant.
10659  *
10660  * Fork - LGPL
10661  * <script type="text/javascript">
10662  */
10663
10664
10665 /**
10666  * @class Roo.CompositeElement
10667  * Standard composite class. Creates a Roo.Element for every element in the collection.
10668  * <br><br>
10669  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10670  * actions will be performed on all the elements in this collection.</b>
10671  * <br><br>
10672  * All methods return <i>this</i> and can be chained.
10673  <pre><code>
10674  var els = Roo.select("#some-el div.some-class", true);
10675  // or select directly from an existing element
10676  var el = Roo.get('some-el');
10677  el.select('div.some-class', true);
10678
10679  els.setWidth(100); // all elements become 100 width
10680  els.hide(true); // all elements fade out and hide
10681  // or
10682  els.setWidth(100).hide(true);
10683  </code></pre>
10684  */
10685 Roo.CompositeElement = function(els){
10686     this.elements = [];
10687     this.addElements(els);
10688 };
10689 Roo.CompositeElement.prototype = {
10690     isComposite: true,
10691     addElements : function(els){
10692         if(!els) return this;
10693         if(typeof els == "string"){
10694             els = Roo.Element.selectorFunction(els);
10695         }
10696         var yels = this.elements;
10697         var index = yels.length-1;
10698         for(var i = 0, len = els.length; i < len; i++) {
10699                 yels[++index] = Roo.get(els[i]);
10700         }
10701         return this;
10702     },
10703
10704     /**
10705     * Clears this composite and adds the elements returned by the passed selector.
10706     * @param {String/Array} els A string CSS selector, an array of elements or an element
10707     * @return {CompositeElement} this
10708     */
10709     fill : function(els){
10710         this.elements = [];
10711         this.add(els);
10712         return this;
10713     },
10714
10715     /**
10716     * Filters this composite to only elements that match the passed selector.
10717     * @param {String} selector A string CSS selector
10718     * @return {CompositeElement} this
10719     */
10720     filter : function(selector){
10721         var els = [];
10722         this.each(function(el){
10723             if(el.is(selector)){
10724                 els[els.length] = el.dom;
10725             }
10726         });
10727         this.fill(els);
10728         return this;
10729     },
10730
10731     invoke : function(fn, args){
10732         var els = this.elements;
10733         for(var i = 0, len = els.length; i < len; i++) {
10734                 Roo.Element.prototype[fn].apply(els[i], args);
10735         }
10736         return this;
10737     },
10738     /**
10739     * Adds elements to this composite.
10740     * @param {String/Array} els A string CSS selector, an array of elements or an element
10741     * @return {CompositeElement} this
10742     */
10743     add : function(els){
10744         if(typeof els == "string"){
10745             this.addElements(Roo.Element.selectorFunction(els));
10746         }else if(els.length !== undefined){
10747             this.addElements(els);
10748         }else{
10749             this.addElements([els]);
10750         }
10751         return this;
10752     },
10753     /**
10754     * Calls the passed function passing (el, this, index) for each element in this composite.
10755     * @param {Function} fn The function to call
10756     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10757     * @return {CompositeElement} this
10758     */
10759     each : function(fn, scope){
10760         var els = this.elements;
10761         for(var i = 0, len = els.length; i < len; i++){
10762             if(fn.call(scope || els[i], els[i], this, i) === false) {
10763                 break;
10764             }
10765         }
10766         return this;
10767     },
10768
10769     /**
10770      * Returns the Element object at the specified index
10771      * @param {Number} index
10772      * @return {Roo.Element}
10773      */
10774     item : function(index){
10775         return this.elements[index] || null;
10776     },
10777
10778     /**
10779      * Returns the first Element
10780      * @return {Roo.Element}
10781      */
10782     first : function(){
10783         return this.item(0);
10784     },
10785
10786     /**
10787      * Returns the last Element
10788      * @return {Roo.Element}
10789      */
10790     last : function(){
10791         return this.item(this.elements.length-1);
10792     },
10793
10794     /**
10795      * Returns the number of elements in this composite
10796      * @return Number
10797      */
10798     getCount : function(){
10799         return this.elements.length;
10800     },
10801
10802     /**
10803      * Returns true if this composite contains the passed element
10804      * @return Boolean
10805      */
10806     contains : function(el){
10807         return this.indexOf(el) !== -1;
10808     },
10809
10810     /**
10811      * Returns true if this composite contains the passed element
10812      * @return Boolean
10813      */
10814     indexOf : function(el){
10815         return this.elements.indexOf(Roo.get(el));
10816     },
10817
10818
10819     /**
10820     * Removes the specified element(s).
10821     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10822     * or an array of any of those.
10823     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10824     * @return {CompositeElement} this
10825     */
10826     removeElement : function(el, removeDom){
10827         if(el instanceof Array){
10828             for(var i = 0, len = el.length; i < len; i++){
10829                 this.removeElement(el[i]);
10830             }
10831             return this;
10832         }
10833         var index = typeof el == 'number' ? el : this.indexOf(el);
10834         if(index !== -1){
10835             if(removeDom){
10836                 var d = this.elements[index];
10837                 if(d.dom){
10838                     d.remove();
10839                 }else{
10840                     d.parentNode.removeChild(d);
10841                 }
10842             }
10843             this.elements.splice(index, 1);
10844         }
10845         return this;
10846     },
10847
10848     /**
10849     * Replaces the specified element with the passed element.
10850     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10851     * to replace.
10852     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10853     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10854     * @return {CompositeElement} this
10855     */
10856     replaceElement : function(el, replacement, domReplace){
10857         var index = typeof el == 'number' ? el : this.indexOf(el);
10858         if(index !== -1){
10859             if(domReplace){
10860                 this.elements[index].replaceWith(replacement);
10861             }else{
10862                 this.elements.splice(index, 1, Roo.get(replacement))
10863             }
10864         }
10865         return this;
10866     },
10867
10868     /**
10869      * Removes all elements.
10870      */
10871     clear : function(){
10872         this.elements = [];
10873     }
10874 };
10875 (function(){
10876     Roo.CompositeElement.createCall = function(proto, fnName){
10877         if(!proto[fnName]){
10878             proto[fnName] = function(){
10879                 return this.invoke(fnName, arguments);
10880             };
10881         }
10882     };
10883     for(var fnName in Roo.Element.prototype){
10884         if(typeof Roo.Element.prototype[fnName] == "function"){
10885             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10886         }
10887     };
10888 })();
10889 /*
10890  * Based on:
10891  * Ext JS Library 1.1.1
10892  * Copyright(c) 2006-2007, Ext JS, LLC.
10893  *
10894  * Originally Released Under LGPL - original licence link has changed is not relivant.
10895  *
10896  * Fork - LGPL
10897  * <script type="text/javascript">
10898  */
10899
10900 /**
10901  * @class Roo.CompositeElementLite
10902  * @extends Roo.CompositeElement
10903  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class");
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class');
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre><br><br>
10915  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10916  * actions will be performed on all the elements in this collection.</b>
10917  */
10918 Roo.CompositeElementLite = function(els){
10919     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10920     this.el = new Roo.Element.Flyweight();
10921 };
10922 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10923     addElements : function(els){
10924         if(els){
10925             if(els instanceof Array){
10926                 this.elements = this.elements.concat(els);
10927             }else{
10928                 var yels = this.elements;
10929                 var index = yels.length-1;
10930                 for(var i = 0, len = els.length; i < len; i++) {
10931                     yels[++index] = els[i];
10932                 }
10933             }
10934         }
10935         return this;
10936     },
10937     invoke : function(fn, args){
10938         var els = this.elements;
10939         var el = this.el;
10940         for(var i = 0, len = els.length; i < len; i++) {
10941             el.dom = els[i];
10942                 Roo.Element.prototype[fn].apply(el, args);
10943         }
10944         return this;
10945     },
10946     /**
10947      * Returns a flyweight Element of the dom element object at the specified index
10948      * @param {Number} index
10949      * @return {Roo.Element}
10950      */
10951     item : function(index){
10952         if(!this.elements[index]){
10953             return null;
10954         }
10955         this.el.dom = this.elements[index];
10956         return this.el;
10957     },
10958
10959     // fixes scope with flyweight
10960     addListener : function(eventName, handler, scope, opt){
10961         var els = this.elements;
10962         for(var i = 0, len = els.length; i < len; i++) {
10963             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10970     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10971     * a reference to the dom node, use el.dom.</b>
10972     * @param {Function} fn The function to call
10973     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10974     * @return {CompositeElement} this
10975     */
10976     each : function(fn, scope){
10977         var els = this.elements;
10978         var el = this.el;
10979         for(var i = 0, len = els.length; i < len; i++){
10980             el.dom = els[i];
10981                 if(fn.call(scope || el, el, this, i) === false){
10982                 break;
10983             }
10984         }
10985         return this;
10986     },
10987
10988     indexOf : function(el){
10989         return this.elements.indexOf(Roo.getDom(el));
10990     },
10991
10992     replaceElement : function(el, replacement, domReplace){
10993         var index = typeof el == 'number' ? el : this.indexOf(el);
10994         if(index !== -1){
10995             replacement = Roo.getDom(replacement);
10996             if(domReplace){
10997                 var d = this.elements[index];
10998                 d.parentNode.insertBefore(replacement, d);
10999                 d.parentNode.removeChild(d);
11000             }
11001             this.elements.splice(index, 1, replacement);
11002         }
11003         return this;
11004     }
11005 });
11006 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11007
11008 /*
11009  * Based on:
11010  * Ext JS Library 1.1.1
11011  * Copyright(c) 2006-2007, Ext JS, LLC.
11012  *
11013  * Originally Released Under LGPL - original licence link has changed is not relivant.
11014  *
11015  * Fork - LGPL
11016  * <script type="text/javascript">
11017  */
11018
11019  
11020
11021 /**
11022  * @class Roo.data.Connection
11023  * @extends Roo.util.Observable
11024  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11025  * either to a configured URL, or to a URL specified at request time.<br><br>
11026  * <p>
11027  * Requests made by this class are asynchronous, and will return immediately. No data from
11028  * the server will be available to the statement immediately following the {@link #request} call.
11029  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11030  * <p>
11031  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11032  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11033  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11034  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11035  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11036  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11037  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11038  * standard DOM methods.
11039  * @constructor
11040  * @param {Object} config a configuration object.
11041  */
11042 Roo.data.Connection = function(config){
11043     Roo.apply(this, config);
11044     this.addEvents({
11045         /**
11046          * @event beforerequest
11047          * Fires before a network request is made to retrieve a data object.
11048          * @param {Connection} conn This Connection object.
11049          * @param {Object} options The options config object passed to the {@link #request} method.
11050          */
11051         "beforerequest" : true,
11052         /**
11053          * @event requestcomplete
11054          * Fires if the request was successfully completed.
11055          * @param {Connection} conn This Connection object.
11056          * @param {Object} response The XHR object containing the response data.
11057          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11058          * @param {Object} options The options config object passed to the {@link #request} method.
11059          */
11060         "requestcomplete" : true,
11061         /**
11062          * @event requestexception
11063          * Fires if an error HTTP status was returned from the server.
11064          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11065          * @param {Connection} conn This Connection object.
11066          * @param {Object} response The XHR object containing the response data.
11067          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11068          * @param {Object} options The options config object passed to the {@link #request} method.
11069          */
11070         "requestexception" : true
11071     });
11072     Roo.data.Connection.superclass.constructor.call(this);
11073 };
11074
11075 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11076     /**
11077      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11078      */
11079     /**
11080      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11081      * extra parameters to each request made by this object. (defaults to undefined)
11082      */
11083     /**
11084      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11085      *  to each request made by this object. (defaults to undefined)
11086      */
11087     /**
11088      * @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)
11089      */
11090     /**
11091      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11092      */
11093     timeout : 30000,
11094     /**
11095      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11096      * @type Boolean
11097      */
11098     autoAbort:false,
11099
11100     /**
11101      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11102      * @type Boolean
11103      */
11104     disableCaching: true,
11105
11106     /**
11107      * Sends an HTTP request to a remote server.
11108      * @param {Object} options An object which may contain the following properties:<ul>
11109      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11110      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11111      * request, a url encoded string or a function to call to get either.</li>
11112      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11113      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11114      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11115      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11116      * <li>options {Object} The parameter to the request call.</li>
11117      * <li>success {Boolean} True if the request succeeded.</li>
11118      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11119      * </ul></li>
11120      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11121      * The callback is passed the following parameters:<ul>
11122      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11123      * <li>options {Object} The parameter to the request call.</li>
11124      * </ul></li>
11125      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11126      * The callback is passed the following parameters:<ul>
11127      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11128      * <li>options {Object} The parameter to the request call.</li>
11129      * </ul></li>
11130      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11131      * for the callback function. Defaults to the browser window.</li>
11132      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11133      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11134      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11135      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11136      * params for the post data. Any params will be appended to the URL.</li>
11137      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11138      * </ul>
11139      * @return {Number} transactionId
11140      */
11141     request : function(o){
11142         if(this.fireEvent("beforerequest", this, o) !== false){
11143             var p = o.params;
11144
11145             if(typeof p == "function"){
11146                 p = p.call(o.scope||window, o);
11147             }
11148             if(typeof p == "object"){
11149                 p = Roo.urlEncode(o.params);
11150             }
11151             if(this.extraParams){
11152                 var extras = Roo.urlEncode(this.extraParams);
11153                 p = p ? (p + '&' + extras) : extras;
11154             }
11155
11156             var url = o.url || this.url;
11157             if(typeof url == 'function'){
11158                 url = url.call(o.scope||window, o);
11159             }
11160
11161             if(o.form){
11162                 var form = Roo.getDom(o.form);
11163                 url = url || form.action;
11164
11165                 var enctype = form.getAttribute("enctype");
11166                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11167                     return this.doFormUpload(o, p, url);
11168                 }
11169                 var f = Roo.lib.Ajax.serializeForm(form);
11170                 p = p ? (p + '&' + f) : f;
11171             }
11172
11173             var hs = o.headers;
11174             if(this.defaultHeaders){
11175                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11176                 if(!o.headers){
11177                     o.headers = hs;
11178                 }
11179             }
11180
11181             var cb = {
11182                 success: this.handleResponse,
11183                 failure: this.handleFailure,
11184                 scope: this,
11185                 argument: {options: o},
11186                 timeout : this.timeout
11187             };
11188
11189             var method = o.method||this.method||(p ? "POST" : "GET");
11190
11191             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11192                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11193             }
11194
11195             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11196                 if(o.autoAbort){
11197                     this.abort();
11198                 }
11199             }else if(this.autoAbort !== false){
11200                 this.abort();
11201             }
11202
11203             if((method == 'GET' && p) || o.xmlData){
11204                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11205                 p = '';
11206             }
11207             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11208             return this.transId;
11209         }else{
11210             Roo.callback(o.callback, o.scope, [o, null, null]);
11211             return null;
11212         }
11213     },
11214
11215     /**
11216      * Determine whether this object has a request outstanding.
11217      * @param {Number} transactionId (Optional) defaults to the last transaction
11218      * @return {Boolean} True if there is an outstanding request.
11219      */
11220     isLoading : function(transId){
11221         if(transId){
11222             return Roo.lib.Ajax.isCallInProgress(transId);
11223         }else{
11224             return this.transId ? true : false;
11225         }
11226     },
11227
11228     /**
11229      * Aborts any outstanding request.
11230      * @param {Number} transactionId (Optional) defaults to the last transaction
11231      */
11232     abort : function(transId){
11233         if(transId || this.isLoading()){
11234             Roo.lib.Ajax.abort(transId || this.transId);
11235         }
11236     },
11237
11238     // private
11239     handleResponse : function(response){
11240         this.transId = false;
11241         var options = response.argument.options;
11242         response.argument = options ? options.argument : null;
11243         this.fireEvent("requestcomplete", this, response, options);
11244         Roo.callback(options.success, options.scope, [response, options]);
11245         Roo.callback(options.callback, options.scope, [options, true, response]);
11246     },
11247
11248     // private
11249     handleFailure : function(response, e){
11250         this.transId = false;
11251         var options = response.argument.options;
11252         response.argument = options ? options.argument : null;
11253         this.fireEvent("requestexception", this, response, options, e);
11254         Roo.callback(options.failure, options.scope, [response, options]);
11255         Roo.callback(options.callback, options.scope, [options, false, response]);
11256     },
11257
11258     // private
11259     doFormUpload : function(o, ps, url){
11260         var id = Roo.id();
11261         var frame = document.createElement('iframe');
11262         frame.id = id;
11263         frame.name = id;
11264         frame.className = 'x-hidden';
11265         if(Roo.isIE){
11266             frame.src = Roo.SSL_SECURE_URL;
11267         }
11268         document.body.appendChild(frame);
11269
11270         if(Roo.isIE){
11271            document.frames[id].name = id;
11272         }
11273
11274         var form = Roo.getDom(o.form);
11275         form.target = id;
11276         form.method = 'POST';
11277         form.enctype = form.encoding = 'multipart/form-data';
11278         if(url){
11279             form.action = url;
11280         }
11281
11282         var hiddens, hd;
11283         if(ps){ // add dynamic params
11284             hiddens = [];
11285             ps = Roo.urlDecode(ps, false);
11286             for(var k in ps){
11287                 if(ps.hasOwnProperty(k)){
11288                     hd = document.createElement('input');
11289                     hd.type = 'hidden';
11290                     hd.name = k;
11291                     hd.value = ps[k];
11292                     form.appendChild(hd);
11293                     hiddens.push(hd);
11294                 }
11295             }
11296         }
11297
11298         function cb(){
11299             var r = {  // bogus response object
11300                 responseText : '',
11301                 responseXML : null
11302             };
11303
11304             r.argument = o ? o.argument : null;
11305
11306             try { //
11307                 var doc;
11308                 if(Roo.isIE){
11309                     doc = frame.contentWindow.document;
11310                 }else {
11311                     doc = (frame.contentDocument || window.frames[id].document);
11312                 }
11313                 if(doc && doc.body){
11314                     r.responseText = doc.body.innerHTML;
11315                 }
11316                 if(doc && doc.XMLDocument){
11317                     r.responseXML = doc.XMLDocument;
11318                 }else {
11319                     r.responseXML = doc;
11320                 }
11321             }
11322             catch(e) {
11323                 // ignore
11324             }
11325
11326             Roo.EventManager.removeListener(frame, 'load', cb, this);
11327
11328             this.fireEvent("requestcomplete", this, r, o);
11329             Roo.callback(o.success, o.scope, [r, o]);
11330             Roo.callback(o.callback, o.scope, [o, true, r]);
11331
11332             setTimeout(function(){document.body.removeChild(frame);}, 100);
11333         }
11334
11335         Roo.EventManager.on(frame, 'load', cb, this);
11336         form.submit();
11337
11338         if(hiddens){ // remove dynamic params
11339             for(var i = 0, len = hiddens.length; i < len; i++){
11340                 form.removeChild(hiddens[i]);
11341             }
11342         }
11343     }
11344 });
11345
11346 /**
11347  * @class Roo.Ajax
11348  * @extends Roo.data.Connection
11349  * Global Ajax request class.
11350  *
11351  * @singleton
11352  */
11353 Roo.Ajax = new Roo.data.Connection({
11354     // fix up the docs
11355    /**
11356      * @cfg {String} url @hide
11357      */
11358     /**
11359      * @cfg {Object} extraParams @hide
11360      */
11361     /**
11362      * @cfg {Object} defaultHeaders @hide
11363      */
11364     /**
11365      * @cfg {String} method (Optional) @hide
11366      */
11367     /**
11368      * @cfg {Number} timeout (Optional) @hide
11369      */
11370     /**
11371      * @cfg {Boolean} autoAbort (Optional) @hide
11372      */
11373
11374     /**
11375      * @cfg {Boolean} disableCaching (Optional) @hide
11376      */
11377
11378     /**
11379      * @property  disableCaching
11380      * True to add a unique cache-buster param to GET requests. (defaults to true)
11381      * @type Boolean
11382      */
11383     /**
11384      * @property  url
11385      * The default URL to be used for requests to the server. (defaults to undefined)
11386      * @type String
11387      */
11388     /**
11389      * @property  extraParams
11390      * An object containing properties which are used as
11391      * extra parameters to each request made by this object. (defaults to undefined)
11392      * @type Object
11393      */
11394     /**
11395      * @property  defaultHeaders
11396      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11397      * @type Object
11398      */
11399     /**
11400      * @property  method
11401      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11402      * @type String
11403      */
11404     /**
11405      * @property  timeout
11406      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11407      * @type Number
11408      */
11409
11410     /**
11411      * @property  autoAbort
11412      * Whether a new request should abort any pending requests. (defaults to false)
11413      * @type Boolean
11414      */
11415     autoAbort : false,
11416
11417     /**
11418      * Serialize the passed form into a url encoded string
11419      * @param {String/HTMLElement} form
11420      * @return {String}
11421      */
11422     serializeForm : function(form){
11423         return Roo.lib.Ajax.serializeForm(form);
11424     }
11425 });/*
11426  * Based on:
11427  * Ext JS Library 1.1.1
11428  * Copyright(c) 2006-2007, Ext JS, LLC.
11429  *
11430  * Originally Released Under LGPL - original licence link has changed is not relivant.
11431  *
11432  * Fork - LGPL
11433  * <script type="text/javascript">
11434  */
11435  
11436 /**
11437  * @class Roo.Ajax
11438  * @extends Roo.data.Connection
11439  * Global Ajax request class.
11440  *
11441  * @instanceOf  Roo.data.Connection
11442  */
11443 Roo.Ajax = new Roo.data.Connection({
11444     // fix up the docs
11445     
11446     /**
11447      * fix up scoping
11448      * @scope Roo.Ajax
11449      */
11450     
11451    /**
11452      * @cfg {String} url @hide
11453      */
11454     /**
11455      * @cfg {Object} extraParams @hide
11456      */
11457     /**
11458      * @cfg {Object} defaultHeaders @hide
11459      */
11460     /**
11461      * @cfg {String} method (Optional) @hide
11462      */
11463     /**
11464      * @cfg {Number} timeout (Optional) @hide
11465      */
11466     /**
11467      * @cfg {Boolean} autoAbort (Optional) @hide
11468      */
11469
11470     /**
11471      * @cfg {Boolean} disableCaching (Optional) @hide
11472      */
11473
11474     /**
11475      * @property  disableCaching
11476      * True to add a unique cache-buster param to GET requests. (defaults to true)
11477      * @type Boolean
11478      */
11479     /**
11480      * @property  url
11481      * The default URL to be used for requests to the server. (defaults to undefined)
11482      * @type String
11483      */
11484     /**
11485      * @property  extraParams
11486      * An object containing properties which are used as
11487      * extra parameters to each request made by this object. (defaults to undefined)
11488      * @type Object
11489      */
11490     /**
11491      * @property  defaultHeaders
11492      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11493      * @type Object
11494      */
11495     /**
11496      * @property  method
11497      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11498      * @type String
11499      */
11500     /**
11501      * @property  timeout
11502      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11503      * @type Number
11504      */
11505
11506     /**
11507      * @property  autoAbort
11508      * Whether a new request should abort any pending requests. (defaults to false)
11509      * @type Boolean
11510      */
11511     autoAbort : false,
11512
11513     /**
11514      * Serialize the passed form into a url encoded string
11515      * @param {String/HTMLElement} form
11516      * @return {String}
11517      */
11518     serializeForm : function(form){
11519         return Roo.lib.Ajax.serializeForm(form);
11520     }
11521 });/*
11522  * Based on:
11523  * Ext JS Library 1.1.1
11524  * Copyright(c) 2006-2007, Ext JS, LLC.
11525  *
11526  * Originally Released Under LGPL - original licence link has changed is not relivant.
11527  *
11528  * Fork - LGPL
11529  * <script type="text/javascript">
11530  */
11531
11532  
11533 /**
11534  * @class Roo.UpdateManager
11535  * @extends Roo.util.Observable
11536  * Provides AJAX-style update for Element object.<br><br>
11537  * Usage:<br>
11538  * <pre><code>
11539  * // Get it from a Roo.Element object
11540  * var el = Roo.get("foo");
11541  * var mgr = el.getUpdateManager();
11542  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11543  * ...
11544  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11545  * <br>
11546  * // or directly (returns the same UpdateManager instance)
11547  * var mgr = new Roo.UpdateManager("myElementId");
11548  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11549  * mgr.on("update", myFcnNeedsToKnow);
11550  * <br>
11551    // short handed call directly from the element object
11552    Roo.get("foo").load({
11553         url: "bar.php",
11554         scripts:true,
11555         params: "for=bar",
11556         text: "Loading Foo..."
11557    });
11558  * </code></pre>
11559  * @constructor
11560  * Create new UpdateManager directly.
11561  * @param {String/HTMLElement/Roo.Element} el The element to update
11562  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11563  */
11564 Roo.UpdateManager = function(el, forceNew){
11565     el = Roo.get(el);
11566     if(!forceNew && el.updateManager){
11567         return el.updateManager;
11568     }
11569     /**
11570      * The Element object
11571      * @type Roo.Element
11572      */
11573     this.el = el;
11574     /**
11575      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11576      * @type String
11577      */
11578     this.defaultUrl = null;
11579
11580     this.addEvents({
11581         /**
11582          * @event beforeupdate
11583          * Fired before an update is made, return false from your handler and the update is cancelled.
11584          * @param {Roo.Element} el
11585          * @param {String/Object/Function} url
11586          * @param {String/Object} params
11587          */
11588         "beforeupdate": true,
11589         /**
11590          * @event update
11591          * Fired after successful update is made.
11592          * @param {Roo.Element} el
11593          * @param {Object} oResponseObject The response Object
11594          */
11595         "update": true,
11596         /**
11597          * @event failure
11598          * Fired on update failure.
11599          * @param {Roo.Element} el
11600          * @param {Object} oResponseObject The response Object
11601          */
11602         "failure": true
11603     });
11604     var d = Roo.UpdateManager.defaults;
11605     /**
11606      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11607      * @type String
11608      */
11609     this.sslBlankUrl = d.sslBlankUrl;
11610     /**
11611      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11612      * @type Boolean
11613      */
11614     this.disableCaching = d.disableCaching;
11615     /**
11616      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11617      * @type String
11618      */
11619     this.indicatorText = d.indicatorText;
11620     /**
11621      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11622      * @type String
11623      */
11624     this.showLoadIndicator = d.showLoadIndicator;
11625     /**
11626      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11627      * @type Number
11628      */
11629     this.timeout = d.timeout;
11630
11631     /**
11632      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11633      * @type Boolean
11634      */
11635     this.loadScripts = d.loadScripts;
11636
11637     /**
11638      * Transaction object of current executing transaction
11639      */
11640     this.transaction = null;
11641
11642     /**
11643      * @private
11644      */
11645     this.autoRefreshProcId = null;
11646     /**
11647      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11648      * @type Function
11649      */
11650     this.refreshDelegate = this.refresh.createDelegate(this);
11651     /**
11652      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11653      * @type Function
11654      */
11655     this.updateDelegate = this.update.createDelegate(this);
11656     /**
11657      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11658      * @type Function
11659      */
11660     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11661     /**
11662      * @private
11663      */
11664     this.successDelegate = this.processSuccess.createDelegate(this);
11665     /**
11666      * @private
11667      */
11668     this.failureDelegate = this.processFailure.createDelegate(this);
11669
11670     if(!this.renderer){
11671      /**
11672       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11673       */
11674     this.renderer = new Roo.UpdateManager.BasicRenderer();
11675     }
11676     
11677     Roo.UpdateManager.superclass.constructor.call(this);
11678 };
11679
11680 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11681     /**
11682      * Get the Element this UpdateManager is bound to
11683      * @return {Roo.Element} The element
11684      */
11685     getEl : function(){
11686         return this.el;
11687     },
11688     /**
11689      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11690      * @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:
11691 <pre><code>
11692 um.update({<br/>
11693     url: "your-url.php",<br/>
11694     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11695     callback: yourFunction,<br/>
11696     scope: yourObject, //(optional scope)  <br/>
11697     discardUrl: false, <br/>
11698     nocache: false,<br/>
11699     text: "Loading...",<br/>
11700     timeout: 30,<br/>
11701     scripts: false<br/>
11702 });
11703 </code></pre>
11704      * The only required property is url. The optional properties nocache, text and scripts
11705      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11706      * @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}
11707      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11708      * @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.
11709      */
11710     update : function(url, params, callback, discardUrl){
11711         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11712             var method = this.method, cfg;
11713             if(typeof url == "object"){ // must be config object
11714                 cfg = url;
11715                 url = cfg.url;
11716                 params = params || cfg.params;
11717                 callback = callback || cfg.callback;
11718                 discardUrl = discardUrl || cfg.discardUrl;
11719                 if(callback && cfg.scope){
11720                     callback = callback.createDelegate(cfg.scope);
11721                 }
11722                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11723                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11724                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11725                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11726                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11727             }
11728             this.showLoading();
11729             if(!discardUrl){
11730                 this.defaultUrl = url;
11731             }
11732             if(typeof url == "function"){
11733                 url = url.call(this);
11734             }
11735
11736             method = method || (params ? "POST" : "GET");
11737             if(method == "GET"){
11738                 url = this.prepareUrl(url);
11739             }
11740
11741             var o = Roo.apply(cfg ||{}, {
11742                 url : url,
11743                 params: params,
11744                 success: this.successDelegate,
11745                 failure: this.failureDelegate,
11746                 callback: undefined,
11747                 timeout: (this.timeout*1000),
11748                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11749             });
11750
11751             this.transaction = Roo.Ajax.request(o);
11752         }
11753     },
11754
11755     /**
11756      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11757      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11758      * @param {String/HTMLElement} form The form Id or form element
11759      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11760      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11761      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11762      */
11763     formUpdate : function(form, url, reset, callback){
11764         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11765             if(typeof url == "function"){
11766                 url = url.call(this);
11767             }
11768             form = Roo.getDom(form);
11769             this.transaction = Roo.Ajax.request({
11770                 form: form,
11771                 url:url,
11772                 success: this.successDelegate,
11773                 failure: this.failureDelegate,
11774                 timeout: (this.timeout*1000),
11775                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11776             });
11777             this.showLoading.defer(1, this);
11778         }
11779     },
11780
11781     /**
11782      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11783      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11784      */
11785     refresh : function(callback){
11786         if(this.defaultUrl == null){
11787             return;
11788         }
11789         this.update(this.defaultUrl, null, callback, true);
11790     },
11791
11792     /**
11793      * Set this element to auto refresh.
11794      * @param {Number} interval How often to update (in seconds).
11795      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11796      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11797      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11798      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11799      */
11800     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11801         if(refreshNow){
11802             this.update(url || this.defaultUrl, params, callback, true);
11803         }
11804         if(this.autoRefreshProcId){
11805             clearInterval(this.autoRefreshProcId);
11806         }
11807         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11808     },
11809
11810     /**
11811      * Stop auto refresh on this element.
11812      */
11813      stopAutoRefresh : function(){
11814         if(this.autoRefreshProcId){
11815             clearInterval(this.autoRefreshProcId);
11816             delete this.autoRefreshProcId;
11817         }
11818     },
11819
11820     isAutoRefreshing : function(){
11821        return this.autoRefreshProcId ? true : false;
11822     },
11823     /**
11824      * Called to update the element to "Loading" state. Override to perform custom action.
11825      */
11826     showLoading : function(){
11827         if(this.showLoadIndicator){
11828             this.el.update(this.indicatorText);
11829         }
11830     },
11831
11832     /**
11833      * Adds unique parameter to query string if disableCaching = true
11834      * @private
11835      */
11836     prepareUrl : function(url){
11837         if(this.disableCaching){
11838             var append = "_dc=" + (new Date().getTime());
11839             if(url.indexOf("?") !== -1){
11840                 url += "&" + append;
11841             }else{
11842                 url += "?" + append;
11843             }
11844         }
11845         return url;
11846     },
11847
11848     /**
11849      * @private
11850      */
11851     processSuccess : function(response){
11852         this.transaction = null;
11853         if(response.argument.form && response.argument.reset){
11854             try{ // put in try/catch since some older FF releases had problems with this
11855                 response.argument.form.reset();
11856             }catch(e){}
11857         }
11858         if(this.loadScripts){
11859             this.renderer.render(this.el, response, this,
11860                 this.updateComplete.createDelegate(this, [response]));
11861         }else{
11862             this.renderer.render(this.el, response, this);
11863             this.updateComplete(response);
11864         }
11865     },
11866
11867     updateComplete : function(response){
11868         this.fireEvent("update", this.el, response);
11869         if(typeof response.argument.callback == "function"){
11870             response.argument.callback(this.el, true, response);
11871         }
11872     },
11873
11874     /**
11875      * @private
11876      */
11877     processFailure : function(response){
11878         this.transaction = null;
11879         this.fireEvent("failure", this.el, response);
11880         if(typeof response.argument.callback == "function"){
11881             response.argument.callback(this.el, false, response);
11882         }
11883     },
11884
11885     /**
11886      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11887      * @param {Object} renderer The object implementing the render() method
11888      */
11889     setRenderer : function(renderer){
11890         this.renderer = renderer;
11891     },
11892
11893     getRenderer : function(){
11894        return this.renderer;
11895     },
11896
11897     /**
11898      * Set the defaultUrl used for updates
11899      * @param {String/Function} defaultUrl The url or a function to call to get the url
11900      */
11901     setDefaultUrl : function(defaultUrl){
11902         this.defaultUrl = defaultUrl;
11903     },
11904
11905     /**
11906      * Aborts the executing transaction
11907      */
11908     abort : function(){
11909         if(this.transaction){
11910             Roo.Ajax.abort(this.transaction);
11911         }
11912     },
11913
11914     /**
11915      * Returns true if an update is in progress
11916      * @return {Boolean}
11917      */
11918     isUpdating : function(){
11919         if(this.transaction){
11920             return Roo.Ajax.isLoading(this.transaction);
11921         }
11922         return false;
11923     }
11924 });
11925
11926 /**
11927  * @class Roo.UpdateManager.defaults
11928  * @static (not really - but it helps the doc tool)
11929  * The defaults collection enables customizing the default properties of UpdateManager
11930  */
11931    Roo.UpdateManager.defaults = {
11932        /**
11933          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11934          * @type Number
11935          */
11936          timeout : 30,
11937
11938          /**
11939          * True to process scripts by default (Defaults to false).
11940          * @type Boolean
11941          */
11942         loadScripts : false,
11943
11944         /**
11945         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11946         * @type String
11947         */
11948         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11949         /**
11950          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11951          * @type Boolean
11952          */
11953         disableCaching : false,
11954         /**
11955          * Whether to show indicatorText when loading (Defaults to true).
11956          * @type Boolean
11957          */
11958         showLoadIndicator : true,
11959         /**
11960          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11961          * @type String
11962          */
11963         indicatorText : '<div class="loading-indicator">Loading...</div>'
11964    };
11965
11966 /**
11967  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11968  *Usage:
11969  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11970  * @param {String/HTMLElement/Roo.Element} el The element to update
11971  * @param {String} url The url
11972  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11973  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11974  * @static
11975  * @deprecated
11976  * @member Roo.UpdateManager
11977  */
11978 Roo.UpdateManager.updateElement = function(el, url, params, options){
11979     var um = Roo.get(el, true).getUpdateManager();
11980     Roo.apply(um, options);
11981     um.update(url, params, options ? options.callback : null);
11982 };
11983 // alias for backwards compat
11984 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11985 /**
11986  * @class Roo.UpdateManager.BasicRenderer
11987  * Default Content renderer. Updates the elements innerHTML with the responseText.
11988  */
11989 Roo.UpdateManager.BasicRenderer = function(){};
11990
11991 Roo.UpdateManager.BasicRenderer.prototype = {
11992     /**
11993      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11994      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11995      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11996      * @param {Roo.Element} el The element being rendered
11997      * @param {Object} response The YUI Connect response object
11998      * @param {UpdateManager} updateManager The calling update manager
11999      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12000      */
12001      render : function(el, response, updateManager, callback){
12002         el.update(response.responseText, updateManager.loadScripts, callback);
12003     }
12004 };
12005 /*
12006  * Based on:
12007  * Ext JS Library 1.1.1
12008  * Copyright(c) 2006-2007, Ext JS, LLC.
12009  *
12010  * Originally Released Under LGPL - original licence link has changed is not relivant.
12011  *
12012  * Fork - LGPL
12013  * <script type="text/javascript">
12014  */
12015
12016 /**
12017  * @class Roo.util.DelayedTask
12018  * Provides a convenient method of performing setTimeout where a new
12019  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12020  * You can use this class to buffer
12021  * the keypress events for a certain number of milliseconds, and perform only if they stop
12022  * for that amount of time.
12023  * @constructor The parameters to this constructor serve as defaults and are not required.
12024  * @param {Function} fn (optional) The default function to timeout
12025  * @param {Object} scope (optional) The default scope of that timeout
12026  * @param {Array} args (optional) The default Array of arguments
12027  */
12028 Roo.util.DelayedTask = function(fn, scope, args){
12029     var id = null, d, t;
12030
12031     var call = function(){
12032         var now = new Date().getTime();
12033         if(now - t >= d){
12034             clearInterval(id);
12035             id = null;
12036             fn.apply(scope, args || []);
12037         }
12038     };
12039     /**
12040      * Cancels any pending timeout and queues a new one
12041      * @param {Number} delay The milliseconds to delay
12042      * @param {Function} newFn (optional) Overrides function passed to constructor
12043      * @param {Object} newScope (optional) Overrides scope passed to constructor
12044      * @param {Array} newArgs (optional) Overrides args passed to constructor
12045      */
12046     this.delay = function(delay, newFn, newScope, newArgs){
12047         if(id && delay != d){
12048             this.cancel();
12049         }
12050         d = delay;
12051         t = new Date().getTime();
12052         fn = newFn || fn;
12053         scope = newScope || scope;
12054         args = newArgs || args;
12055         if(!id){
12056             id = setInterval(call, d);
12057         }
12058     };
12059
12060     /**
12061      * Cancel the last queued timeout
12062      */
12063     this.cancel = function(){
12064         if(id){
12065             clearInterval(id);
12066             id = null;
12067         }
12068     };
12069 };/*
12070  * Based on:
12071  * Ext JS Library 1.1.1
12072  * Copyright(c) 2006-2007, Ext JS, LLC.
12073  *
12074  * Originally Released Under LGPL - original licence link has changed is not relivant.
12075  *
12076  * Fork - LGPL
12077  * <script type="text/javascript">
12078  */
12079  
12080  
12081 Roo.util.TaskRunner = function(interval){
12082     interval = interval || 10;
12083     var tasks = [], removeQueue = [];
12084     var id = 0;
12085     var running = false;
12086
12087     var stopThread = function(){
12088         running = false;
12089         clearInterval(id);
12090         id = 0;
12091     };
12092
12093     var startThread = function(){
12094         if(!running){
12095             running = true;
12096             id = setInterval(runTasks, interval);
12097         }
12098     };
12099
12100     var removeTask = function(task){
12101         removeQueue.push(task);
12102         if(task.onStop){
12103             task.onStop();
12104         }
12105     };
12106
12107     var runTasks = function(){
12108         if(removeQueue.length > 0){
12109             for(var i = 0, len = removeQueue.length; i < len; i++){
12110                 tasks.remove(removeQueue[i]);
12111             }
12112             removeQueue = [];
12113             if(tasks.length < 1){
12114                 stopThread();
12115                 return;
12116             }
12117         }
12118         var now = new Date().getTime();
12119         for(var i = 0, len = tasks.length; i < len; ++i){
12120             var t = tasks[i];
12121             var itime = now - t.taskRunTime;
12122             if(t.interval <= itime){
12123                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12124                 t.taskRunTime = now;
12125                 if(rt === false || t.taskRunCount === t.repeat){
12126                     removeTask(t);
12127                     return;
12128                 }
12129             }
12130             if(t.duration && t.duration <= (now - t.taskStartTime)){
12131                 removeTask(t);
12132             }
12133         }
12134     };
12135
12136     /**
12137      * Queues a new task.
12138      * @param {Object} task
12139      */
12140     this.start = function(task){
12141         tasks.push(task);
12142         task.taskStartTime = new Date().getTime();
12143         task.taskRunTime = 0;
12144         task.taskRunCount = 0;
12145         startThread();
12146         return task;
12147     };
12148
12149     this.stop = function(task){
12150         removeTask(task);
12151         return task;
12152     };
12153
12154     this.stopAll = function(){
12155         stopThread();
12156         for(var i = 0, len = tasks.length; i < len; i++){
12157             if(tasks[i].onStop){
12158                 tasks[i].onStop();
12159             }
12160         }
12161         tasks = [];
12162         removeQueue = [];
12163     };
12164 };
12165
12166 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12167  * Based on:
12168  * Ext JS Library 1.1.1
12169  * Copyright(c) 2006-2007, Ext JS, LLC.
12170  *
12171  * Originally Released Under LGPL - original licence link has changed is not relivant.
12172  *
12173  * Fork - LGPL
12174  * <script type="text/javascript">
12175  */
12176
12177  
12178 /**
12179  * @class Roo.util.MixedCollection
12180  * @extends Roo.util.Observable
12181  * A Collection class that maintains both numeric indexes and keys and exposes events.
12182  * @constructor
12183  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12184  * collection (defaults to false)
12185  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12186  * and return the key value for that item.  This is used when available to look up the key on items that
12187  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12188  * equivalent to providing an implementation for the {@link #getKey} method.
12189  */
12190 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12191     this.items = [];
12192     this.map = {};
12193     this.keys = [];
12194     this.length = 0;
12195     this.addEvents({
12196         /**
12197          * @event clear
12198          * Fires when the collection is cleared.
12199          */
12200         "clear" : true,
12201         /**
12202          * @event add
12203          * Fires when an item is added to the collection.
12204          * @param {Number} index The index at which the item was added.
12205          * @param {Object} o The item added.
12206          * @param {String} key The key associated with the added item.
12207          */
12208         "add" : true,
12209         /**
12210          * @event replace
12211          * Fires when an item is replaced in the collection.
12212          * @param {String} key he key associated with the new added.
12213          * @param {Object} old The item being replaced.
12214          * @param {Object} new The new item.
12215          */
12216         "replace" : true,
12217         /**
12218          * @event remove
12219          * Fires when an item is removed from the collection.
12220          * @param {Object} o The item being removed.
12221          * @param {String} key (optional) The key associated with the removed item.
12222          */
12223         "remove" : true,
12224         "sort" : true
12225     });
12226     this.allowFunctions = allowFunctions === true;
12227     if(keyFn){
12228         this.getKey = keyFn;
12229     }
12230     Roo.util.MixedCollection.superclass.constructor.call(this);
12231 };
12232
12233 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12234     allowFunctions : false,
12235     
12236 /**
12237  * Adds an item to the collection.
12238  * @param {String} key The key to associate with the item
12239  * @param {Object} o The item to add.
12240  * @return {Object} The item added.
12241  */
12242     add : function(key, o){
12243         if(arguments.length == 1){
12244             o = arguments[0];
12245             key = this.getKey(o);
12246         }
12247         if(typeof key == "undefined" || key === null){
12248             this.length++;
12249             this.items.push(o);
12250             this.keys.push(null);
12251         }else{
12252             var old = this.map[key];
12253             if(old){
12254                 return this.replace(key, o);
12255             }
12256             this.length++;
12257             this.items.push(o);
12258             this.map[key] = o;
12259             this.keys.push(key);
12260         }
12261         this.fireEvent("add", this.length-1, o, key);
12262         return o;
12263     },
12264    
12265 /**
12266   * MixedCollection has a generic way to fetch keys if you implement getKey.
12267 <pre><code>
12268 // normal way
12269 var mc = new Roo.util.MixedCollection();
12270 mc.add(someEl.dom.id, someEl);
12271 mc.add(otherEl.dom.id, otherEl);
12272 //and so on
12273
12274 // using getKey
12275 var mc = new Roo.util.MixedCollection();
12276 mc.getKey = function(el){
12277    return el.dom.id;
12278 };
12279 mc.add(someEl);
12280 mc.add(otherEl);
12281
12282 // or via the constructor
12283 var mc = new Roo.util.MixedCollection(false, function(el){
12284    return el.dom.id;
12285 });
12286 mc.add(someEl);
12287 mc.add(otherEl);
12288 </code></pre>
12289  * @param o {Object} The item for which to find the key.
12290  * @return {Object} The key for the passed item.
12291  */
12292     getKey : function(o){
12293          return o.id; 
12294     },
12295    
12296 /**
12297  * Replaces an item in the collection.
12298  * @param {String} key The key associated with the item to replace, or the item to replace.
12299  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12300  * @return {Object}  The new item.
12301  */
12302     replace : function(key, o){
12303         if(arguments.length == 1){
12304             o = arguments[0];
12305             key = this.getKey(o);
12306         }
12307         var old = this.item(key);
12308         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12309              return this.add(key, o);
12310         }
12311         var index = this.indexOfKey(key);
12312         this.items[index] = o;
12313         this.map[key] = o;
12314         this.fireEvent("replace", key, old, o);
12315         return o;
12316     },
12317    
12318 /**
12319  * Adds all elements of an Array or an Object to the collection.
12320  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12321  * an Array of values, each of which are added to the collection.
12322  */
12323     addAll : function(objs){
12324         if(arguments.length > 1 || objs instanceof Array){
12325             var args = arguments.length > 1 ? arguments : objs;
12326             for(var i = 0, len = args.length; i < len; i++){
12327                 this.add(args[i]);
12328             }
12329         }else{
12330             for(var key in objs){
12331                 if(this.allowFunctions || typeof objs[key] != "function"){
12332                     this.add(key, objs[key]);
12333                 }
12334             }
12335         }
12336     },
12337    
12338 /**
12339  * Executes the specified function once for every item in the collection, passing each
12340  * item as the first and only parameter. returning false from the function will stop the iteration.
12341  * @param {Function} fn The function to execute for each item.
12342  * @param {Object} scope (optional) The scope in which to execute the function.
12343  */
12344     each : function(fn, scope){
12345         var items = [].concat(this.items); // each safe for removal
12346         for(var i = 0, len = items.length; i < len; i++){
12347             if(fn.call(scope || items[i], items[i], i, len) === false){
12348                 break;
12349             }
12350         }
12351     },
12352    
12353 /**
12354  * Executes the specified function once for every key in the collection, passing each
12355  * key, and its associated item as the first two parameters.
12356  * @param {Function} fn The function to execute for each item.
12357  * @param {Object} scope (optional) The scope in which to execute the function.
12358  */
12359     eachKey : function(fn, scope){
12360         for(var i = 0, len = this.keys.length; i < len; i++){
12361             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12362         }
12363     },
12364    
12365 /**
12366  * Returns the first item in the collection which elicits a true return value from the
12367  * passed selection function.
12368  * @param {Function} fn The selection function to execute for each item.
12369  * @param {Object} scope (optional) The scope in which to execute the function.
12370  * @return {Object} The first item in the collection which returned true from the selection function.
12371  */
12372     find : function(fn, scope){
12373         for(var i = 0, len = this.items.length; i < len; i++){
12374             if(fn.call(scope || window, this.items[i], this.keys[i])){
12375                 return this.items[i];
12376             }
12377         }
12378         return null;
12379     },
12380    
12381 /**
12382  * Inserts an item at the specified index in the collection.
12383  * @param {Number} index The index to insert the item at.
12384  * @param {String} key The key to associate with the new item, or the item itself.
12385  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12386  * @return {Object} The item inserted.
12387  */
12388     insert : function(index, key, o){
12389         if(arguments.length == 2){
12390             o = arguments[1];
12391             key = this.getKey(o);
12392         }
12393         if(index >= this.length){
12394             return this.add(key, o);
12395         }
12396         this.length++;
12397         this.items.splice(index, 0, o);
12398         if(typeof key != "undefined" && key != null){
12399             this.map[key] = o;
12400         }
12401         this.keys.splice(index, 0, key);
12402         this.fireEvent("add", index, o, key);
12403         return o;
12404     },
12405    
12406 /**
12407  * Removed an item from the collection.
12408  * @param {Object} o The item to remove.
12409  * @return {Object} The item removed.
12410  */
12411     remove : function(o){
12412         return this.removeAt(this.indexOf(o));
12413     },
12414    
12415 /**
12416  * Remove an item from a specified index in the collection.
12417  * @param {Number} index The index within the collection of the item to remove.
12418  */
12419     removeAt : function(index){
12420         if(index < this.length && index >= 0){
12421             this.length--;
12422             var o = this.items[index];
12423             this.items.splice(index, 1);
12424             var key = this.keys[index];
12425             if(typeof key != "undefined"){
12426                 delete this.map[key];
12427             }
12428             this.keys.splice(index, 1);
12429             this.fireEvent("remove", o, key);
12430         }
12431     },
12432    
12433 /**
12434  * Removed an item associated with the passed key fom the collection.
12435  * @param {String} key The key of the item to remove.
12436  */
12437     removeKey : function(key){
12438         return this.removeAt(this.indexOfKey(key));
12439     },
12440    
12441 /**
12442  * Returns the number of items in the collection.
12443  * @return {Number} the number of items in the collection.
12444  */
12445     getCount : function(){
12446         return this.length; 
12447     },
12448    
12449 /**
12450  * Returns index within the collection of the passed Object.
12451  * @param {Object} o The item to find the index of.
12452  * @return {Number} index of the item.
12453  */
12454     indexOf : function(o){
12455         if(!this.items.indexOf){
12456             for(var i = 0, len = this.items.length; i < len; i++){
12457                 if(this.items[i] == o) return i;
12458             }
12459             return -1;
12460         }else{
12461             return this.items.indexOf(o);
12462         }
12463     },
12464    
12465 /**
12466  * Returns index within the collection of the passed key.
12467  * @param {String} key The key to find the index of.
12468  * @return {Number} index of the key.
12469  */
12470     indexOfKey : function(key){
12471         if(!this.keys.indexOf){
12472             for(var i = 0, len = this.keys.length; i < len; i++){
12473                 if(this.keys[i] == key) return i;
12474             }
12475             return -1;
12476         }else{
12477             return this.keys.indexOf(key);
12478         }
12479     },
12480    
12481 /**
12482  * Returns the item associated with the passed key OR index. Key has priority over index.
12483  * @param {String/Number} key The key or index of the item.
12484  * @return {Object} The item associated with the passed key.
12485  */
12486     item : function(key){
12487         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12488         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12489     },
12490     
12491 /**
12492  * Returns the item at the specified index.
12493  * @param {Number} index The index of the item.
12494  * @return {Object}
12495  */
12496     itemAt : function(index){
12497         return this.items[index];
12498     },
12499     
12500 /**
12501  * Returns the item associated with the passed key.
12502  * @param {String/Number} key The key of the item.
12503  * @return {Object} The item associated with the passed key.
12504  */
12505     key : function(key){
12506         return this.map[key];
12507     },
12508    
12509 /**
12510  * Returns true if the collection contains the passed Object as an item.
12511  * @param {Object} o  The Object to look for in the collection.
12512  * @return {Boolean} True if the collection contains the Object as an item.
12513  */
12514     contains : function(o){
12515         return this.indexOf(o) != -1;
12516     },
12517    
12518 /**
12519  * Returns true if the collection contains the passed Object as a key.
12520  * @param {String} key The key to look for in the collection.
12521  * @return {Boolean} True if the collection contains the Object as a key.
12522  */
12523     containsKey : function(key){
12524         return typeof this.map[key] != "undefined";
12525     },
12526    
12527 /**
12528  * Removes all items from the collection.
12529  */
12530     clear : function(){
12531         this.length = 0;
12532         this.items = [];
12533         this.keys = [];
12534         this.map = {};
12535         this.fireEvent("clear");
12536     },
12537    
12538 /**
12539  * Returns the first item in the collection.
12540  * @return {Object} the first item in the collection..
12541  */
12542     first : function(){
12543         return this.items[0]; 
12544     },
12545    
12546 /**
12547  * Returns the last item in the collection.
12548  * @return {Object} the last item in the collection..
12549  */
12550     last : function(){
12551         return this.items[this.length-1];   
12552     },
12553     
12554     _sort : function(property, dir, fn){
12555         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12556         fn = fn || function(a, b){
12557             return a-b;
12558         };
12559         var c = [], k = this.keys, items = this.items;
12560         for(var i = 0, len = items.length; i < len; i++){
12561             c[c.length] = {key: k[i], value: items[i], index: i};
12562         }
12563         c.sort(function(a, b){
12564             var v = fn(a[property], b[property]) * dsc;
12565             if(v == 0){
12566                 v = (a.index < b.index ? -1 : 1);
12567             }
12568             return v;
12569         });
12570         for(var i = 0, len = c.length; i < len; i++){
12571             items[i] = c[i].value;
12572             k[i] = c[i].key;
12573         }
12574         this.fireEvent("sort", this);
12575     },
12576     
12577     /**
12578      * Sorts this collection with the passed comparison function
12579      * @param {String} direction (optional) "ASC" or "DESC"
12580      * @param {Function} fn (optional) comparison function
12581      */
12582     sort : function(dir, fn){
12583         this._sort("value", dir, fn);
12584     },
12585     
12586     /**
12587      * Sorts this collection by keys
12588      * @param {String} direction (optional) "ASC" or "DESC"
12589      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12590      */
12591     keySort : function(dir, fn){
12592         this._sort("key", dir, fn || function(a, b){
12593             return String(a).toUpperCase()-String(b).toUpperCase();
12594         });
12595     },
12596     
12597     /**
12598      * Returns a range of items in this collection
12599      * @param {Number} startIndex (optional) defaults to 0
12600      * @param {Number} endIndex (optional) default to the last item
12601      * @return {Array} An array of items
12602      */
12603     getRange : function(start, end){
12604         var items = this.items;
12605         if(items.length < 1){
12606             return [];
12607         }
12608         start = start || 0;
12609         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12610         var r = [];
12611         if(start <= end){
12612             for(var i = start; i <= end; i++) {
12613                     r[r.length] = items[i];
12614             }
12615         }else{
12616             for(var i = start; i >= end; i--) {
12617                     r[r.length] = items[i];
12618             }
12619         }
12620         return r;
12621     },
12622         
12623     /**
12624      * Filter the <i>objects</i> in this collection by a specific property. 
12625      * Returns a new collection that has been filtered.
12626      * @param {String} property A property on your objects
12627      * @param {String/RegExp} value Either string that the property values 
12628      * should start with or a RegExp to test against the property
12629      * @return {MixedCollection} The new filtered collection
12630      */
12631     filter : function(property, value){
12632         if(!value.exec){ // not a regex
12633             value = String(value);
12634             if(value.length == 0){
12635                 return this.clone();
12636             }
12637             value = new RegExp("^" + Roo.escapeRe(value), "i");
12638         }
12639         return this.filterBy(function(o){
12640             return o && value.test(o[property]);
12641         });
12642         },
12643     
12644     /**
12645      * Filter by a function. * Returns a new collection that has been filtered.
12646      * The passed function will be called with each 
12647      * object in the collection. If the function returns true, the value is included 
12648      * otherwise it is filtered.
12649      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12650      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12651      * @return {MixedCollection} The new filtered collection
12652      */
12653     filterBy : function(fn, scope){
12654         var r = new Roo.util.MixedCollection();
12655         r.getKey = this.getKey;
12656         var k = this.keys, it = this.items;
12657         for(var i = 0, len = it.length; i < len; i++){
12658             if(fn.call(scope||this, it[i], k[i])){
12659                                 r.add(k[i], it[i]);
12660                         }
12661         }
12662         return r;
12663     },
12664     
12665     /**
12666      * Creates a duplicate of this collection
12667      * @return {MixedCollection}
12668      */
12669     clone : function(){
12670         var r = new Roo.util.MixedCollection();
12671         var k = this.keys, it = this.items;
12672         for(var i = 0, len = it.length; i < len; i++){
12673             r.add(k[i], it[i]);
12674         }
12675         r.getKey = this.getKey;
12676         return r;
12677     }
12678 });
12679 /**
12680  * Returns the item associated with the passed key or index.
12681  * @method
12682  * @param {String/Number} key The key or index of the item.
12683  * @return {Object} The item associated with the passed key.
12684  */
12685 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12686  * Based on:
12687  * Ext JS Library 1.1.1
12688  * Copyright(c) 2006-2007, Ext JS, LLC.
12689  *
12690  * Originally Released Under LGPL - original licence link has changed is not relivant.
12691  *
12692  * Fork - LGPL
12693  * <script type="text/javascript">
12694  */
12695 /**
12696  * @class Roo.util.JSON
12697  * Modified version of Douglas Crockford"s json.js that doesn"t
12698  * mess with the Object prototype 
12699  * http://www.json.org/js.html
12700  * @singleton
12701  */
12702 Roo.util.JSON = new (function(){
12703     var useHasOwn = {}.hasOwnProperty ? true : false;
12704     
12705     // crashes Safari in some instances
12706     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12707     
12708     var pad = function(n) {
12709         return n < 10 ? "0" + n : n;
12710     };
12711     
12712     var m = {
12713         "\b": '\\b',
12714         "\t": '\\t',
12715         "\n": '\\n',
12716         "\f": '\\f',
12717         "\r": '\\r',
12718         '"' : '\\"',
12719         "\\": '\\\\'
12720     };
12721
12722     var encodeString = function(s){
12723         if (/["\\\x00-\x1f]/.test(s)) {
12724             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12725                 var c = m[b];
12726                 if(c){
12727                     return c;
12728                 }
12729                 c = b.charCodeAt();
12730                 return "\\u00" +
12731                     Math.floor(c / 16).toString(16) +
12732                     (c % 16).toString(16);
12733             }) + '"';
12734         }
12735         return '"' + s + '"';
12736     };
12737     
12738     var encodeArray = function(o){
12739         var a = ["["], b, i, l = o.length, v;
12740             for (i = 0; i < l; i += 1) {
12741                 v = o[i];
12742                 switch (typeof v) {
12743                     case "undefined":
12744                     case "function":
12745                     case "unknown":
12746                         break;
12747                     default:
12748                         if (b) {
12749                             a.push(',');
12750                         }
12751                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12752                         b = true;
12753                 }
12754             }
12755             a.push("]");
12756             return a.join("");
12757     };
12758     
12759     var encodeDate = function(o){
12760         return '"' + o.getFullYear() + "-" +
12761                 pad(o.getMonth() + 1) + "-" +
12762                 pad(o.getDate()) + "T" +
12763                 pad(o.getHours()) + ":" +
12764                 pad(o.getMinutes()) + ":" +
12765                 pad(o.getSeconds()) + '"';
12766     };
12767     
12768     /**
12769      * Encodes an Object, Array or other value
12770      * @param {Mixed} o The variable to encode
12771      * @return {String} The JSON string
12772      */
12773     this.encode = function(o)
12774     {
12775         // should this be extended to fully wrap stringify..
12776         
12777         if(typeof o == "undefined" || o === null){
12778             return "null";
12779         }else if(o instanceof Array){
12780             return encodeArray(o);
12781         }else if(o instanceof Date){
12782             return encodeDate(o);
12783         }else if(typeof o == "string"){
12784             return encodeString(o);
12785         }else if(typeof o == "number"){
12786             return isFinite(o) ? String(o) : "null";
12787         }else if(typeof o == "boolean"){
12788             return String(o);
12789         }else {
12790             var a = ["{"], b, i, v;
12791             for (i in o) {
12792                 if(!useHasOwn || o.hasOwnProperty(i)) {
12793                     v = o[i];
12794                     switch (typeof v) {
12795                     case "undefined":
12796                     case "function":
12797                     case "unknown":
12798                         break;
12799                     default:
12800                         if(b){
12801                             a.push(',');
12802                         }
12803                         a.push(this.encode(i), ":",
12804                                 v === null ? "null" : this.encode(v));
12805                         b = true;
12806                     }
12807                 }
12808             }
12809             a.push("}");
12810             return a.join("");
12811         }
12812     };
12813     
12814     /**
12815      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12816      * @param {String} json The JSON string
12817      * @return {Object} The resulting object
12818      */
12819     this.decode = function(json){
12820         
12821         return  /** eval:var:json */ eval("(" + json + ')');
12822     };
12823 })();
12824 /** 
12825  * Shorthand for {@link Roo.util.JSON#encode}
12826  * @member Roo encode 
12827  * @method */
12828 Roo.encode = JSON && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12829 /** 
12830  * Shorthand for {@link Roo.util.JSON#decode}
12831  * @member Roo decode 
12832  * @method */
12833 Roo.decode = JSON && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12834 /*
12835  * Based on:
12836  * Ext JS Library 1.1.1
12837  * Copyright(c) 2006-2007, Ext JS, LLC.
12838  *
12839  * Originally Released Under LGPL - original licence link has changed is not relivant.
12840  *
12841  * Fork - LGPL
12842  * <script type="text/javascript">
12843  */
12844  
12845 /**
12846  * @class Roo.util.Format
12847  * Reusable data formatting functions
12848  * @singleton
12849  */
12850 Roo.util.Format = function(){
12851     var trimRe = /^\s+|\s+$/g;
12852     return {
12853         /**
12854          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12855          * @param {String} value The string to truncate
12856          * @param {Number} length The maximum length to allow before truncating
12857          * @return {String} The converted text
12858          */
12859         ellipsis : function(value, len){
12860             if(value && value.length > len){
12861                 return value.substr(0, len-3)+"...";
12862             }
12863             return value;
12864         },
12865
12866         /**
12867          * Checks a reference and converts it to empty string if it is undefined
12868          * @param {Mixed} value Reference to check
12869          * @return {Mixed} Empty string if converted, otherwise the original value
12870          */
12871         undef : function(value){
12872             return typeof value != "undefined" ? value : "";
12873         },
12874
12875         /**
12876          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12877          * @param {String} value The string to encode
12878          * @return {String} The encoded text
12879          */
12880         htmlEncode : function(value){
12881             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12882         },
12883
12884         /**
12885          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12886          * @param {String} value The string to decode
12887          * @return {String} The decoded text
12888          */
12889         htmlDecode : function(value){
12890             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12891         },
12892
12893         /**
12894          * Trims any whitespace from either side of a string
12895          * @param {String} value The text to trim
12896          * @return {String} The trimmed text
12897          */
12898         trim : function(value){
12899             return String(value).replace(trimRe, "");
12900         },
12901
12902         /**
12903          * Returns a substring from within an original string
12904          * @param {String} value The original text
12905          * @param {Number} start The start index of the substring
12906          * @param {Number} length The length of the substring
12907          * @return {String} The substring
12908          */
12909         substr : function(value, start, length){
12910             return String(value).substr(start, length);
12911         },
12912
12913         /**
12914          * Converts a string to all lower case letters
12915          * @param {String} value The text to convert
12916          * @return {String} The converted text
12917          */
12918         lowercase : function(value){
12919             return String(value).toLowerCase();
12920         },
12921
12922         /**
12923          * Converts a string to all upper case letters
12924          * @param {String} value The text to convert
12925          * @return {String} The converted text
12926          */
12927         uppercase : function(value){
12928             return String(value).toUpperCase();
12929         },
12930
12931         /**
12932          * Converts the first character only of a string to upper case
12933          * @param {String} value The text to convert
12934          * @return {String} The converted text
12935          */
12936         capitalize : function(value){
12937             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12938         },
12939
12940         // private
12941         call : function(value, fn){
12942             if(arguments.length > 2){
12943                 var args = Array.prototype.slice.call(arguments, 2);
12944                 args.unshift(value);
12945                  
12946                 return /** eval:var:value */  eval(fn).apply(window, args);
12947             }else{
12948                 /** eval:var:value */
12949                 return /** eval:var:value */ eval(fn).call(window, value);
12950             }
12951         },
12952
12953        
12954         /**
12955          * safer version of Math.toFixed..??/
12956          * @param {Number/String} value The numeric value to format
12957          * @param {Number/String} value Decimal places 
12958          * @return {String} The formatted currency string
12959          */
12960         toFixed : function(v, n)
12961         {
12962             // why not use to fixed - precision is buggered???
12963             if (!n) {
12964                 return Math.round(v-0);
12965             }
12966             var fact = Math.pow(10,n+1);
12967             v = (Math.round((v-0)*fact))/fact;
12968             var z = (''+fact).substring(2);
12969             if (v == Math.floor(v)) {
12970                 return Math.floor(v) + '.' + z;
12971             }
12972             
12973             // now just padd decimals..
12974             var ps = String(v).split('.');
12975             var fd = (ps[1] + z);
12976             var r = fd.substring(0,n); 
12977             var rm = fd.substring(n); 
12978             if (rm < 5) {
12979                 return ps[0] + '.' + r;
12980             }
12981             r*=1; // turn it into a number;
12982             r++;
12983             if (String(r).length != n) {
12984                 ps[0]*=1;
12985                 ps[0]++;
12986                 r = String(r).substring(1); // chop the end off.
12987             }
12988             
12989             return ps[0] + '.' + r;
12990              
12991         },
12992         
12993         /**
12994          * Format a number as US currency
12995          * @param {Number/String} value The numeric value to format
12996          * @return {String} The formatted currency string
12997          */
12998         usMoney : function(v){
12999             v = (Math.round((v-0)*100))/100;
13000             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13001             v = String(v);
13002             var ps = v.split('.');
13003             var whole = ps[0];
13004             var sub = ps[1] ? '.'+ ps[1] : '.00';
13005             var r = /(\d+)(\d{3})/;
13006             while (r.test(whole)) {
13007                 whole = whole.replace(r, '$1' + ',' + '$2');
13008             }
13009             return "$" + whole + sub ;
13010         },
13011         
13012         /**
13013          * Parse a value into a formatted date using the specified format pattern.
13014          * @param {Mixed} value The value to format
13015          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13016          * @return {String} The formatted date string
13017          */
13018         date : function(v, format){
13019             if(!v){
13020                 return "";
13021             }
13022             if(!(v instanceof Date)){
13023                 v = new Date(Date.parse(v));
13024             }
13025             return v.dateFormat(format || "m/d/Y");
13026         },
13027
13028         /**
13029          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13030          * @param {String} format Any valid date format string
13031          * @return {Function} The date formatting function
13032          */
13033         dateRenderer : function(format){
13034             return function(v){
13035                 return Roo.util.Format.date(v, format);  
13036             };
13037         },
13038
13039         // private
13040         stripTagsRE : /<\/?[^>]+>/gi,
13041         
13042         /**
13043          * Strips all HTML tags
13044          * @param {Mixed} value The text from which to strip tags
13045          * @return {String} The stripped text
13046          */
13047         stripTags : function(v){
13048             return !v ? v : String(v).replace(this.stripTagsRE, "");
13049         }
13050     };
13051 }();/*
13052  * Based on:
13053  * Ext JS Library 1.1.1
13054  * Copyright(c) 2006-2007, Ext JS, LLC.
13055  *
13056  * Originally Released Under LGPL - original licence link has changed is not relivant.
13057  *
13058  * Fork - LGPL
13059  * <script type="text/javascript">
13060  */
13061
13062
13063  
13064
13065 /**
13066  * @class Roo.MasterTemplate
13067  * @extends Roo.Template
13068  * Provides a template that can have child templates. The syntax is:
13069 <pre><code>
13070 var t = new Roo.MasterTemplate(
13071         '&lt;select name="{name}"&gt;',
13072                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13073         '&lt;/select&gt;'
13074 );
13075 t.add('options', {value: 'foo', text: 'bar'});
13076 // or you can add multiple child elements in one shot
13077 t.addAll('options', [
13078     {value: 'foo', text: 'bar'},
13079     {value: 'foo2', text: 'bar2'},
13080     {value: 'foo3', text: 'bar3'}
13081 ]);
13082 // then append, applying the master template values
13083 t.append('my-form', {name: 'my-select'});
13084 </code></pre>
13085 * A name attribute for the child template is not required if you have only one child
13086 * template or you want to refer to them by index.
13087  */
13088 Roo.MasterTemplate = function(){
13089     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13090     this.originalHtml = this.html;
13091     var st = {};
13092     var m, re = this.subTemplateRe;
13093     re.lastIndex = 0;
13094     var subIndex = 0;
13095     while(m = re.exec(this.html)){
13096         var name = m[1], content = m[2];
13097         st[subIndex] = {
13098             name: name,
13099             index: subIndex,
13100             buffer: [],
13101             tpl : new Roo.Template(content)
13102         };
13103         if(name){
13104             st[name] = st[subIndex];
13105         }
13106         st[subIndex].tpl.compile();
13107         st[subIndex].tpl.call = this.call.createDelegate(this);
13108         subIndex++;
13109     }
13110     this.subCount = subIndex;
13111     this.subs = st;
13112 };
13113 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13114     /**
13115     * The regular expression used to match sub templates
13116     * @type RegExp
13117     * @property
13118     */
13119     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13120
13121     /**
13122      * Applies the passed values to a child template.
13123      * @param {String/Number} name (optional) The name or index of the child template
13124      * @param {Array/Object} values The values to be applied to the template
13125      * @return {MasterTemplate} this
13126      */
13127      add : function(name, values){
13128         if(arguments.length == 1){
13129             values = arguments[0];
13130             name = 0;
13131         }
13132         var s = this.subs[name];
13133         s.buffer[s.buffer.length] = s.tpl.apply(values);
13134         return this;
13135     },
13136
13137     /**
13138      * Applies all the passed values to a child template.
13139      * @param {String/Number} name (optional) The name or index of the child template
13140      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13141      * @param {Boolean} reset (optional) True to reset the template first
13142      * @return {MasterTemplate} this
13143      */
13144     fill : function(name, values, reset){
13145         var a = arguments;
13146         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13147             values = a[0];
13148             name = 0;
13149             reset = a[1];
13150         }
13151         if(reset){
13152             this.reset();
13153         }
13154         for(var i = 0, len = values.length; i < len; i++){
13155             this.add(name, values[i]);
13156         }
13157         return this;
13158     },
13159
13160     /**
13161      * Resets the template for reuse
13162      * @return {MasterTemplate} this
13163      */
13164      reset : function(){
13165         var s = this.subs;
13166         for(var i = 0; i < this.subCount; i++){
13167             s[i].buffer = [];
13168         }
13169         return this;
13170     },
13171
13172     applyTemplate : function(values){
13173         var s = this.subs;
13174         var replaceIndex = -1;
13175         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13176             return s[++replaceIndex].buffer.join("");
13177         });
13178         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13179     },
13180
13181     apply : function(){
13182         return this.applyTemplate.apply(this, arguments);
13183     },
13184
13185     compile : function(){return this;}
13186 });
13187
13188 /**
13189  * Alias for fill().
13190  * @method
13191  */
13192 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13193  /**
13194  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13195  * var tpl = Roo.MasterTemplate.from('element-id');
13196  * @param {String/HTMLElement} el
13197  * @param {Object} config
13198  * @static
13199  */
13200 Roo.MasterTemplate.from = function(el, config){
13201     el = Roo.getDom(el);
13202     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13203 };/*
13204  * Based on:
13205  * Ext JS Library 1.1.1
13206  * Copyright(c) 2006-2007, Ext JS, LLC.
13207  *
13208  * Originally Released Under LGPL - original licence link has changed is not relivant.
13209  *
13210  * Fork - LGPL
13211  * <script type="text/javascript">
13212  */
13213
13214  
13215 /**
13216  * @class Roo.util.CSS
13217  * Utility class for manipulating CSS rules
13218  * @singleton
13219  */
13220 Roo.util.CSS = function(){
13221         var rules = null;
13222         var doc = document;
13223
13224     var camelRe = /(-[a-z])/gi;
13225     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13226
13227    return {
13228    /**
13229     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13230     * tag and appended to the HEAD of the document.
13231     * @param {String|Object} cssText The text containing the css rules
13232     * @param {String} id An id to add to the stylesheet for later removal
13233     * @return {StyleSheet}
13234     */
13235     createStyleSheet : function(cssText, id){
13236         var ss;
13237         var head = doc.getElementsByTagName("head")[0];
13238         var nrules = doc.createElement("style");
13239         nrules.setAttribute("type", "text/css");
13240         if(id){
13241             nrules.setAttribute("id", id);
13242         }
13243         if (typeof(cssText) != 'string') {
13244             // support object maps..
13245             // not sure if this a good idea.. 
13246             // perhaps it should be merged with the general css handling
13247             // and handle js style props.
13248             var cssTextNew = [];
13249             for(var n in cssText) {
13250                 var citems = [];
13251                 for(var k in cssText[n]) {
13252                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13253                 }
13254                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13255                 
13256             }
13257             cssText = cssTextNew.join("\n");
13258             
13259         }
13260        
13261        
13262        if(Roo.isIE){
13263            head.appendChild(nrules);
13264            ss = nrules.styleSheet;
13265            ss.cssText = cssText;
13266        }else{
13267            try{
13268                 nrules.appendChild(doc.createTextNode(cssText));
13269            }catch(e){
13270                nrules.cssText = cssText; 
13271            }
13272            head.appendChild(nrules);
13273            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13274        }
13275        this.cacheStyleSheet(ss);
13276        return ss;
13277    },
13278
13279    /**
13280     * Removes a style or link tag by id
13281     * @param {String} id The id of the tag
13282     */
13283    removeStyleSheet : function(id){
13284        var existing = doc.getElementById(id);
13285        if(existing){
13286            existing.parentNode.removeChild(existing);
13287        }
13288    },
13289
13290    /**
13291     * Dynamically swaps an existing stylesheet reference for a new one
13292     * @param {String} id The id of an existing link tag to remove
13293     * @param {String} url The href of the new stylesheet to include
13294     */
13295    swapStyleSheet : function(id, url){
13296        this.removeStyleSheet(id);
13297        var ss = doc.createElement("link");
13298        ss.setAttribute("rel", "stylesheet");
13299        ss.setAttribute("type", "text/css");
13300        ss.setAttribute("id", id);
13301        ss.setAttribute("href", url);
13302        doc.getElementsByTagName("head")[0].appendChild(ss);
13303    },
13304    
13305    /**
13306     * Refresh the rule cache if you have dynamically added stylesheets
13307     * @return {Object} An object (hash) of rules indexed by selector
13308     */
13309    refreshCache : function(){
13310        return this.getRules(true);
13311    },
13312
13313    // private
13314    cacheStyleSheet : function(stylesheet){
13315        if(!rules){
13316            rules = {};
13317        }
13318        try{// try catch for cross domain access issue
13319            var ssRules = stylesheet.cssRules || stylesheet.rules;
13320            for(var j = ssRules.length-1; j >= 0; --j){
13321                rules[ssRules[j].selectorText] = ssRules[j];
13322            }
13323        }catch(e){}
13324    },
13325    
13326    /**
13327     * Gets all css rules for the document
13328     * @param {Boolean} refreshCache true to refresh the internal cache
13329     * @return {Object} An object (hash) of rules indexed by selector
13330     */
13331    getRules : function(refreshCache){
13332                 if(rules == null || refreshCache){
13333                         rules = {};
13334                         var ds = doc.styleSheets;
13335                         for(var i =0, len = ds.length; i < len; i++){
13336                             try{
13337                         this.cacheStyleSheet(ds[i]);
13338                     }catch(e){} 
13339                 }
13340                 }
13341                 return rules;
13342         },
13343         
13344         /**
13345     * Gets an an individual CSS rule by selector(s)
13346     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13347     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13348     * @return {CSSRule} The CSS rule or null if one is not found
13349     */
13350    getRule : function(selector, refreshCache){
13351                 var rs = this.getRules(refreshCache);
13352                 if(!(selector instanceof Array)){
13353                     return rs[selector];
13354                 }
13355                 for(var i = 0; i < selector.length; i++){
13356                         if(rs[selector[i]]){
13357                                 return rs[selector[i]];
13358                         }
13359                 }
13360                 return null;
13361         },
13362         
13363         
13364         /**
13365     * Updates a rule property
13366     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13367     * @param {String} property The css property
13368     * @param {String} value The new value for the property
13369     * @return {Boolean} true If a rule was found and updated
13370     */
13371    updateRule : function(selector, property, value){
13372                 if(!(selector instanceof Array)){
13373                         var rule = this.getRule(selector);
13374                         if(rule){
13375                                 rule.style[property.replace(camelRe, camelFn)] = value;
13376                                 return true;
13377                         }
13378                 }else{
13379                         for(var i = 0; i < selector.length; i++){
13380                                 if(this.updateRule(selector[i], property, value)){
13381                                         return true;
13382                                 }
13383                         }
13384                 }
13385                 return false;
13386         }
13387    };   
13388 }();/*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398
13399  
13400
13401 /**
13402  * @class Roo.util.ClickRepeater
13403  * @extends Roo.util.Observable
13404  * 
13405  * A wrapper class which can be applied to any element. Fires a "click" event while the
13406  * mouse is pressed. The interval between firings may be specified in the config but
13407  * defaults to 10 milliseconds.
13408  * 
13409  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13410  * 
13411  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13412  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13413  * Similar to an autorepeat key delay.
13414  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13415  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13416  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13417  *           "interval" and "delay" are ignored. "immediate" is honored.
13418  * @cfg {Boolean} preventDefault True to prevent the default click event
13419  * @cfg {Boolean} stopDefault True to stop the default click event
13420  * 
13421  * @history
13422  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13423  *     2007-02-02 jvs Renamed to ClickRepeater
13424  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13425  *
13426  *  @constructor
13427  * @param {String/HTMLElement/Element} el The element to listen on
13428  * @param {Object} config
13429  **/
13430 Roo.util.ClickRepeater = function(el, config)
13431 {
13432     this.el = Roo.get(el);
13433     this.el.unselectable();
13434
13435     Roo.apply(this, config);
13436
13437     this.addEvents({
13438     /**
13439      * @event mousedown
13440      * Fires when the mouse button is depressed.
13441      * @param {Roo.util.ClickRepeater} this
13442      */
13443         "mousedown" : true,
13444     /**
13445      * @event click
13446      * Fires on a specified interval during the time the element is pressed.
13447      * @param {Roo.util.ClickRepeater} this
13448      */
13449         "click" : true,
13450     /**
13451      * @event mouseup
13452      * Fires when the mouse key is released.
13453      * @param {Roo.util.ClickRepeater} this
13454      */
13455         "mouseup" : true
13456     });
13457
13458     this.el.on("mousedown", this.handleMouseDown, this);
13459     if(this.preventDefault || this.stopDefault){
13460         this.el.on("click", function(e){
13461             if(this.preventDefault){
13462                 e.preventDefault();
13463             }
13464             if(this.stopDefault){
13465                 e.stopEvent();
13466             }
13467         }, this);
13468     }
13469
13470     // allow inline handler
13471     if(this.handler){
13472         this.on("click", this.handler,  this.scope || this);
13473     }
13474
13475     Roo.util.ClickRepeater.superclass.constructor.call(this);
13476 };
13477
13478 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13479     interval : 20,
13480     delay: 250,
13481     preventDefault : true,
13482     stopDefault : false,
13483     timer : 0,
13484
13485     // private
13486     handleMouseDown : function(){
13487         clearTimeout(this.timer);
13488         this.el.blur();
13489         if(this.pressClass){
13490             this.el.addClass(this.pressClass);
13491         }
13492         this.mousedownTime = new Date();
13493
13494         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13495         this.el.on("mouseout", this.handleMouseOut, this);
13496
13497         this.fireEvent("mousedown", this);
13498         this.fireEvent("click", this);
13499         
13500         this.timer = this.click.defer(this.delay || this.interval, this);
13501     },
13502
13503     // private
13504     click : function(){
13505         this.fireEvent("click", this);
13506         this.timer = this.click.defer(this.getInterval(), this);
13507     },
13508
13509     // private
13510     getInterval: function(){
13511         if(!this.accelerate){
13512             return this.interval;
13513         }
13514         var pressTime = this.mousedownTime.getElapsed();
13515         if(pressTime < 500){
13516             return 400;
13517         }else if(pressTime < 1700){
13518             return 320;
13519         }else if(pressTime < 2600){
13520             return 250;
13521         }else if(pressTime < 3500){
13522             return 180;
13523         }else if(pressTime < 4400){
13524             return 140;
13525         }else if(pressTime < 5300){
13526             return 80;
13527         }else if(pressTime < 6200){
13528             return 50;
13529         }else{
13530             return 10;
13531         }
13532     },
13533
13534     // private
13535     handleMouseOut : function(){
13536         clearTimeout(this.timer);
13537         if(this.pressClass){
13538             this.el.removeClass(this.pressClass);
13539         }
13540         this.el.on("mouseover", this.handleMouseReturn, this);
13541     },
13542
13543     // private
13544     handleMouseReturn : function(){
13545         this.el.un("mouseover", this.handleMouseReturn);
13546         if(this.pressClass){
13547             this.el.addClass(this.pressClass);
13548         }
13549         this.click();
13550     },
13551
13552     // private
13553     handleMouseUp : function(){
13554         clearTimeout(this.timer);
13555         this.el.un("mouseover", this.handleMouseReturn);
13556         this.el.un("mouseout", this.handleMouseOut);
13557         Roo.get(document).un("mouseup", this.handleMouseUp);
13558         this.el.removeClass(this.pressClass);
13559         this.fireEvent("mouseup", this);
13560     }
13561 });/*
13562  * Based on:
13563  * Ext JS Library 1.1.1
13564  * Copyright(c) 2006-2007, Ext JS, LLC.
13565  *
13566  * Originally Released Under LGPL - original licence link has changed is not relivant.
13567  *
13568  * Fork - LGPL
13569  * <script type="text/javascript">
13570  */
13571
13572  
13573 /**
13574  * @class Roo.KeyNav
13575  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13576  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13577  * way to implement custom navigation schemes for any UI component.</p>
13578  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13579  * pageUp, pageDown, del, home, end.  Usage:</p>
13580  <pre><code>
13581 var nav = new Roo.KeyNav("my-element", {
13582     "left" : function(e){
13583         this.moveLeft(e.ctrlKey);
13584     },
13585     "right" : function(e){
13586         this.moveRight(e.ctrlKey);
13587     },
13588     "enter" : function(e){
13589         this.save();
13590     },
13591     scope : this
13592 });
13593 </code></pre>
13594  * @constructor
13595  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13596  * @param {Object} config The config
13597  */
13598 Roo.KeyNav = function(el, config){
13599     this.el = Roo.get(el);
13600     Roo.apply(this, config);
13601     if(!this.disabled){
13602         this.disabled = true;
13603         this.enable();
13604     }
13605 };
13606
13607 Roo.KeyNav.prototype = {
13608     /**
13609      * @cfg {Boolean} disabled
13610      * True to disable this KeyNav instance (defaults to false)
13611      */
13612     disabled : false,
13613     /**
13614      * @cfg {String} defaultEventAction
13615      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13616      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13617      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13618      */
13619     defaultEventAction: "stopEvent",
13620     /**
13621      * @cfg {Boolean} forceKeyDown
13622      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13623      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13624      * handle keydown instead of keypress.
13625      */
13626     forceKeyDown : false,
13627
13628     // private
13629     prepareEvent : function(e){
13630         var k = e.getKey();
13631         var h = this.keyToHandler[k];
13632         //if(h && this[h]){
13633         //    e.stopPropagation();
13634         //}
13635         if(Roo.isSafari && h && k >= 37 && k <= 40){
13636             e.stopEvent();
13637         }
13638     },
13639
13640     // private
13641     relay : function(e){
13642         var k = e.getKey();
13643         var h = this.keyToHandler[k];
13644         if(h && this[h]){
13645             if(this.doRelay(e, this[h], h) !== true){
13646                 e[this.defaultEventAction]();
13647             }
13648         }
13649     },
13650
13651     // private
13652     doRelay : function(e, h, hname){
13653         return h.call(this.scope || this, e);
13654     },
13655
13656     // possible handlers
13657     enter : false,
13658     left : false,
13659     right : false,
13660     up : false,
13661     down : false,
13662     tab : false,
13663     esc : false,
13664     pageUp : false,
13665     pageDown : false,
13666     del : false,
13667     home : false,
13668     end : false,
13669
13670     // quick lookup hash
13671     keyToHandler : {
13672         37 : "left",
13673         39 : "right",
13674         38 : "up",
13675         40 : "down",
13676         33 : "pageUp",
13677         34 : "pageDown",
13678         46 : "del",
13679         36 : "home",
13680         35 : "end",
13681         13 : "enter",
13682         27 : "esc",
13683         9  : "tab"
13684     },
13685
13686         /**
13687          * Enable this KeyNav
13688          */
13689         enable: function(){
13690                 if(this.disabled){
13691             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13692             // the EventObject will normalize Safari automatically
13693             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13694                 this.el.on("keydown", this.relay,  this);
13695             }else{
13696                 this.el.on("keydown", this.prepareEvent,  this);
13697                 this.el.on("keypress", this.relay,  this);
13698             }
13699                     this.disabled = false;
13700                 }
13701         },
13702
13703         /**
13704          * Disable this KeyNav
13705          */
13706         disable: function(){
13707                 if(!this.disabled){
13708                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13709                 this.el.un("keydown", this.relay);
13710             }else{
13711                 this.el.un("keydown", this.prepareEvent);
13712                 this.el.un("keypress", this.relay);
13713             }
13714                     this.disabled = true;
13715                 }
13716         }
13717 };/*
13718  * Based on:
13719  * Ext JS Library 1.1.1
13720  * Copyright(c) 2006-2007, Ext JS, LLC.
13721  *
13722  * Originally Released Under LGPL - original licence link has changed is not relivant.
13723  *
13724  * Fork - LGPL
13725  * <script type="text/javascript">
13726  */
13727
13728  
13729 /**
13730  * @class Roo.KeyMap
13731  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13732  * The constructor accepts the same config object as defined by {@link #addBinding}.
13733  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13734  * combination it will call the function with this signature (if the match is a multi-key
13735  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13736  * A KeyMap can also handle a string representation of keys.<br />
13737  * Usage:
13738  <pre><code>
13739 // map one key by key code
13740 var map = new Roo.KeyMap("my-element", {
13741     key: 13, // or Roo.EventObject.ENTER
13742     fn: myHandler,
13743     scope: myObject
13744 });
13745
13746 // map multiple keys to one action by string
13747 var map = new Roo.KeyMap("my-element", {
13748     key: "a\r\n\t",
13749     fn: myHandler,
13750     scope: myObject
13751 });
13752
13753 // map multiple keys to multiple actions by strings and array of codes
13754 var map = new Roo.KeyMap("my-element", [
13755     {
13756         key: [10,13],
13757         fn: function(){ alert("Return was pressed"); }
13758     }, {
13759         key: "abc",
13760         fn: function(){ alert('a, b or c was pressed'); }
13761     }, {
13762         key: "\t",
13763         ctrl:true,
13764         shift:true,
13765         fn: function(){ alert('Control + shift + tab was pressed.'); }
13766     }
13767 ]);
13768 </code></pre>
13769  * <b>Note: A KeyMap starts enabled</b>
13770  * @constructor
13771  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13772  * @param {Object} config The config (see {@link #addBinding})
13773  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13774  */
13775 Roo.KeyMap = function(el, config, eventName){
13776     this.el  = Roo.get(el);
13777     this.eventName = eventName || "keydown";
13778     this.bindings = [];
13779     if(config){
13780         this.addBinding(config);
13781     }
13782     this.enable();
13783 };
13784
13785 Roo.KeyMap.prototype = {
13786     /**
13787      * True to stop the event from bubbling and prevent the default browser action if the
13788      * key was handled by the KeyMap (defaults to false)
13789      * @type Boolean
13790      */
13791     stopEvent : false,
13792
13793     /**
13794      * Add a new binding to this KeyMap. The following config object properties are supported:
13795      * <pre>
13796 Property    Type             Description
13797 ----------  ---------------  ----------------------------------------------------------------------
13798 key         String/Array     A single keycode or an array of keycodes to handle
13799 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13800 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13801 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13802 fn          Function         The function to call when KeyMap finds the expected key combination
13803 scope       Object           The scope of the callback function
13804 </pre>
13805      *
13806      * Usage:
13807      * <pre><code>
13808 // Create a KeyMap
13809 var map = new Roo.KeyMap(document, {
13810     key: Roo.EventObject.ENTER,
13811     fn: handleKey,
13812     scope: this
13813 });
13814
13815 //Add a new binding to the existing KeyMap later
13816 map.addBinding({
13817     key: 'abc',
13818     shift: true,
13819     fn: handleKey,
13820     scope: this
13821 });
13822 </code></pre>
13823      * @param {Object/Array} config A single KeyMap config or an array of configs
13824      */
13825         addBinding : function(config){
13826         if(config instanceof Array){
13827             for(var i = 0, len = config.length; i < len; i++){
13828                 this.addBinding(config[i]);
13829             }
13830             return;
13831         }
13832         var keyCode = config.key,
13833             shift = config.shift, 
13834             ctrl = config.ctrl, 
13835             alt = config.alt,
13836             fn = config.fn,
13837             scope = config.scope;
13838         if(typeof keyCode == "string"){
13839             var ks = [];
13840             var keyString = keyCode.toUpperCase();
13841             for(var j = 0, len = keyString.length; j < len; j++){
13842                 ks.push(keyString.charCodeAt(j));
13843             }
13844             keyCode = ks;
13845         }
13846         var keyArray = keyCode instanceof Array;
13847         var handler = function(e){
13848             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13849                 var k = e.getKey();
13850                 if(keyArray){
13851                     for(var i = 0, len = keyCode.length; i < len; i++){
13852                         if(keyCode[i] == k){
13853                           if(this.stopEvent){
13854                               e.stopEvent();
13855                           }
13856                           fn.call(scope || window, k, e);
13857                           return;
13858                         }
13859                     }
13860                 }else{
13861                     if(k == keyCode){
13862                         if(this.stopEvent){
13863                            e.stopEvent();
13864                         }
13865                         fn.call(scope || window, k, e);
13866                     }
13867                 }
13868             }
13869         };
13870         this.bindings.push(handler);  
13871         },
13872
13873     /**
13874      * Shorthand for adding a single key listener
13875      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13876      * following options:
13877      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13878      * @param {Function} fn The function to call
13879      * @param {Object} scope (optional) The scope of the function
13880      */
13881     on : function(key, fn, scope){
13882         var keyCode, shift, ctrl, alt;
13883         if(typeof key == "object" && !(key instanceof Array)){
13884             keyCode = key.key;
13885             shift = key.shift;
13886             ctrl = key.ctrl;
13887             alt = key.alt;
13888         }else{
13889             keyCode = key;
13890         }
13891         this.addBinding({
13892             key: keyCode,
13893             shift: shift,
13894             ctrl: ctrl,
13895             alt: alt,
13896             fn: fn,
13897             scope: scope
13898         })
13899     },
13900
13901     // private
13902     handleKeyDown : function(e){
13903             if(this.enabled){ //just in case
13904             var b = this.bindings;
13905             for(var i = 0, len = b.length; i < len; i++){
13906                 b[i].call(this, e);
13907             }
13908             }
13909         },
13910         
13911         /**
13912          * Returns true if this KeyMap is enabled
13913          * @return {Boolean} 
13914          */
13915         isEnabled : function(){
13916             return this.enabled;  
13917         },
13918         
13919         /**
13920          * Enables this KeyMap
13921          */
13922         enable: function(){
13923                 if(!this.enabled){
13924                     this.el.on(this.eventName, this.handleKeyDown, this);
13925                     this.enabled = true;
13926                 }
13927         },
13928
13929         /**
13930          * Disable this KeyMap
13931          */
13932         disable: function(){
13933                 if(this.enabled){
13934                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13935                     this.enabled = false;
13936                 }
13937         }
13938 };/*
13939  * Based on:
13940  * Ext JS Library 1.1.1
13941  * Copyright(c) 2006-2007, Ext JS, LLC.
13942  *
13943  * Originally Released Under LGPL - original licence link has changed is not relivant.
13944  *
13945  * Fork - LGPL
13946  * <script type="text/javascript">
13947  */
13948
13949  
13950 /**
13951  * @class Roo.util.TextMetrics
13952  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13953  * wide, in pixels, a given block of text will be.
13954  * @singleton
13955  */
13956 Roo.util.TextMetrics = function(){
13957     var shared;
13958     return {
13959         /**
13960          * Measures the size of the specified text
13961          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13962          * that can affect the size of the rendered text
13963          * @param {String} text The text to measure
13964          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13965          * in order to accurately measure the text height
13966          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13967          */
13968         measure : function(el, text, fixedWidth){
13969             if(!shared){
13970                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13971             }
13972             shared.bind(el);
13973             shared.setFixedWidth(fixedWidth || 'auto');
13974             return shared.getSize(text);
13975         },
13976
13977         /**
13978          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13979          * the overhead of multiple calls to initialize the style properties on each measurement.
13980          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13981          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13982          * in order to accurately measure the text height
13983          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13984          */
13985         createInstance : function(el, fixedWidth){
13986             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13987         }
13988     };
13989 }();
13990
13991  
13992
13993 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13994     var ml = new Roo.Element(document.createElement('div'));
13995     document.body.appendChild(ml.dom);
13996     ml.position('absolute');
13997     ml.setLeftTop(-1000, -1000);
13998     ml.hide();
13999
14000     if(fixedWidth){
14001         ml.setWidth(fixedWidth);
14002     }
14003      
14004     var instance = {
14005         /**
14006          * Returns the size of the specified text based on the internal element's style and width properties
14007          * @memberOf Roo.util.TextMetrics.Instance#
14008          * @param {String} text The text to measure
14009          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14010          */
14011         getSize : function(text){
14012             ml.update(text);
14013             var s = ml.getSize();
14014             ml.update('');
14015             return s;
14016         },
14017
14018         /**
14019          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14020          * that can affect the size of the rendered text
14021          * @memberOf Roo.util.TextMetrics.Instance#
14022          * @param {String/HTMLElement} el The element, dom node or id
14023          */
14024         bind : function(el){
14025             ml.setStyle(
14026                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14027             );
14028         },
14029
14030         /**
14031          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14032          * to set a fixed width in order to accurately measure the text height.
14033          * @memberOf Roo.util.TextMetrics.Instance#
14034          * @param {Number} width The width to set on the element
14035          */
14036         setFixedWidth : function(width){
14037             ml.setWidth(width);
14038         },
14039
14040         /**
14041          * Returns the measured width of the specified text
14042          * @memberOf Roo.util.TextMetrics.Instance#
14043          * @param {String} text The text to measure
14044          * @return {Number} width The width in pixels
14045          */
14046         getWidth : function(text){
14047             ml.dom.style.width = 'auto';
14048             return this.getSize(text).width;
14049         },
14050
14051         /**
14052          * Returns the measured height of the specified text.  For multiline text, be sure to call
14053          * {@link #setFixedWidth} if necessary.
14054          * @memberOf Roo.util.TextMetrics.Instance#
14055          * @param {String} text The text to measure
14056          * @return {Number} height The height in pixels
14057          */
14058         getHeight : function(text){
14059             return this.getSize(text).height;
14060         }
14061     };
14062
14063     instance.bind(bindTo);
14064
14065     return instance;
14066 };
14067
14068 // backwards compat
14069 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14070  * Based on:
14071  * Ext JS Library 1.1.1
14072  * Copyright(c) 2006-2007, Ext JS, LLC.
14073  *
14074  * Originally Released Under LGPL - original licence link has changed is not relivant.
14075  *
14076  * Fork - LGPL
14077  * <script type="text/javascript">
14078  */
14079
14080 /**
14081  * @class Roo.state.Provider
14082  * Abstract base class for state provider implementations. This class provides methods
14083  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14084  * Provider interface.
14085  */
14086 Roo.state.Provider = function(){
14087     /**
14088      * @event statechange
14089      * Fires when a state change occurs.
14090      * @param {Provider} this This state provider
14091      * @param {String} key The state key which was changed
14092      * @param {String} value The encoded value for the state
14093      */
14094     this.addEvents({
14095         "statechange": true
14096     });
14097     this.state = {};
14098     Roo.state.Provider.superclass.constructor.call(this);
14099 };
14100 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14101     /**
14102      * Returns the current value for a key
14103      * @param {String} name The key name
14104      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14105      * @return {Mixed} The state data
14106      */
14107     get : function(name, defaultValue){
14108         return typeof this.state[name] == "undefined" ?
14109             defaultValue : this.state[name];
14110     },
14111     
14112     /**
14113      * Clears a value from the state
14114      * @param {String} name The key name
14115      */
14116     clear : function(name){
14117         delete this.state[name];
14118         this.fireEvent("statechange", this, name, null);
14119     },
14120     
14121     /**
14122      * Sets the value for a key
14123      * @param {String} name The key name
14124      * @param {Mixed} value The value to set
14125      */
14126     set : function(name, value){
14127         this.state[name] = value;
14128         this.fireEvent("statechange", this, name, value);
14129     },
14130     
14131     /**
14132      * Decodes a string previously encoded with {@link #encodeValue}.
14133      * @param {String} value The value to decode
14134      * @return {Mixed} The decoded value
14135      */
14136     decodeValue : function(cookie){
14137         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14138         var matches = re.exec(unescape(cookie));
14139         if(!matches || !matches[1]) return; // non state cookie
14140         var type = matches[1];
14141         var v = matches[2];
14142         switch(type){
14143             case "n":
14144                 return parseFloat(v);
14145             case "d":
14146                 return new Date(Date.parse(v));
14147             case "b":
14148                 return (v == "1");
14149             case "a":
14150                 var all = [];
14151                 var values = v.split("^");
14152                 for(var i = 0, len = values.length; i < len; i++){
14153                     all.push(this.decodeValue(values[i]));
14154                 }
14155                 return all;
14156            case "o":
14157                 var all = {};
14158                 var values = v.split("^");
14159                 for(var i = 0, len = values.length; i < len; i++){
14160                     var kv = values[i].split("=");
14161                     all[kv[0]] = this.decodeValue(kv[1]);
14162                 }
14163                 return all;
14164            default:
14165                 return v;
14166         }
14167     },
14168     
14169     /**
14170      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14171      * @param {Mixed} value The value to encode
14172      * @return {String} The encoded value
14173      */
14174     encodeValue : function(v){
14175         var enc;
14176         if(typeof v == "number"){
14177             enc = "n:" + v;
14178         }else if(typeof v == "boolean"){
14179             enc = "b:" + (v ? "1" : "0");
14180         }else if(v instanceof Date){
14181             enc = "d:" + v.toGMTString();
14182         }else if(v instanceof Array){
14183             var flat = "";
14184             for(var i = 0, len = v.length; i < len; i++){
14185                 flat += this.encodeValue(v[i]);
14186                 if(i != len-1) flat += "^";
14187             }
14188             enc = "a:" + flat;
14189         }else if(typeof v == "object"){
14190             var flat = "";
14191             for(var key in v){
14192                 if(typeof v[key] != "function"){
14193                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14194                 }
14195             }
14196             enc = "o:" + flat.substring(0, flat.length-1);
14197         }else{
14198             enc = "s:" + v;
14199         }
14200         return escape(enc);        
14201     }
14202 });
14203
14204 /*
14205  * Based on:
14206  * Ext JS Library 1.1.1
14207  * Copyright(c) 2006-2007, Ext JS, LLC.
14208  *
14209  * Originally Released Under LGPL - original licence link has changed is not relivant.
14210  *
14211  * Fork - LGPL
14212  * <script type="text/javascript">
14213  */
14214 /**
14215  * @class Roo.state.Manager
14216  * This is the global state manager. By default all components that are "state aware" check this class
14217  * for state information if you don't pass them a custom state provider. In order for this class
14218  * to be useful, it must be initialized with a provider when your application initializes.
14219  <pre><code>
14220 // in your initialization function
14221 init : function(){
14222    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14223    ...
14224    // supposed you have a {@link Roo.BorderLayout}
14225    var layout = new Roo.BorderLayout(...);
14226    layout.restoreState();
14227    // or a {Roo.BasicDialog}
14228    var dialog = new Roo.BasicDialog(...);
14229    dialog.restoreState();
14230  </code></pre>
14231  * @singleton
14232  */
14233 Roo.state.Manager = function(){
14234     var provider = new Roo.state.Provider();
14235     
14236     return {
14237         /**
14238          * Configures the default state provider for your application
14239          * @param {Provider} stateProvider The state provider to set
14240          */
14241         setProvider : function(stateProvider){
14242             provider = stateProvider;
14243         },
14244         
14245         /**
14246          * Returns the current value for a key
14247          * @param {String} name The key name
14248          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14249          * @return {Mixed} The state data
14250          */
14251         get : function(key, defaultValue){
14252             return provider.get(key, defaultValue);
14253         },
14254         
14255         /**
14256          * Sets the value for a key
14257          * @param {String} name The key name
14258          * @param {Mixed} value The state data
14259          */
14260          set : function(key, value){
14261             provider.set(key, value);
14262         },
14263         
14264         /**
14265          * Clears a value from the state
14266          * @param {String} name The key name
14267          */
14268         clear : function(key){
14269             provider.clear(key);
14270         },
14271         
14272         /**
14273          * Gets the currently configured state provider
14274          * @return {Provider} The state provider
14275          */
14276         getProvider : function(){
14277             return provider;
14278         }
14279     };
14280 }();
14281 /*
14282  * Based on:
14283  * Ext JS Library 1.1.1
14284  * Copyright(c) 2006-2007, Ext JS, LLC.
14285  *
14286  * Originally Released Under LGPL - original licence link has changed is not relivant.
14287  *
14288  * Fork - LGPL
14289  * <script type="text/javascript">
14290  */
14291 /**
14292  * @class Roo.state.CookieProvider
14293  * @extends Roo.state.Provider
14294  * The default Provider implementation which saves state via cookies.
14295  * <br />Usage:
14296  <pre><code>
14297    var cp = new Roo.state.CookieProvider({
14298        path: "/cgi-bin/",
14299        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14300        domain: "roojs.com"
14301    })
14302    Roo.state.Manager.setProvider(cp);
14303  </code></pre>
14304  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14305  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14306  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14307  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14308  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14309  * domain the page is running on including the 'www' like 'www.roojs.com')
14310  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14311  * @constructor
14312  * Create a new CookieProvider
14313  * @param {Object} config The configuration object
14314  */
14315 Roo.state.CookieProvider = function(config){
14316     Roo.state.CookieProvider.superclass.constructor.call(this);
14317     this.path = "/";
14318     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14319     this.domain = null;
14320     this.secure = false;
14321     Roo.apply(this, config);
14322     this.state = this.readCookies();
14323 };
14324
14325 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14326     // private
14327     set : function(name, value){
14328         if(typeof value == "undefined" || value === null){
14329             this.clear(name);
14330             return;
14331         }
14332         this.setCookie(name, value);
14333         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14334     },
14335
14336     // private
14337     clear : function(name){
14338         this.clearCookie(name);
14339         Roo.state.CookieProvider.superclass.clear.call(this, name);
14340     },
14341
14342     // private
14343     readCookies : function(){
14344         var cookies = {};
14345         var c = document.cookie + ";";
14346         var re = /\s?(.*?)=(.*?);/g;
14347         var matches;
14348         while((matches = re.exec(c)) != null){
14349             var name = matches[1];
14350             var value = matches[2];
14351             if(name && name.substring(0,3) == "ys-"){
14352                 cookies[name.substr(3)] = this.decodeValue(value);
14353             }
14354         }
14355         return cookies;
14356     },
14357
14358     // private
14359     setCookie : function(name, value){
14360         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14361            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14362            ((this.path == null) ? "" : ("; path=" + this.path)) +
14363            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14364            ((this.secure == true) ? "; secure" : "");
14365     },
14366
14367     // private
14368     clearCookie : function(name){
14369         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14370            ((this.path == null) ? "" : ("; path=" + this.path)) +
14371            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14372            ((this.secure == true) ? "; secure" : "");
14373     }
14374 });/*
14375  * Based on:
14376  * Ext JS Library 1.1.1
14377  * Copyright(c) 2006-2007, Ext JS, LLC.
14378  *
14379  * Originally Released Under LGPL - original licence link has changed is not relivant.
14380  *
14381  * Fork - LGPL
14382  * <script type="text/javascript">
14383  */
14384
14385
14386
14387 /*
14388  * These classes are derivatives of the similarly named classes in the YUI Library.
14389  * The original license:
14390  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14391  * Code licensed under the BSD License:
14392  * http://developer.yahoo.net/yui/license.txt
14393  */
14394
14395 (function() {
14396
14397 var Event=Roo.EventManager;
14398 var Dom=Roo.lib.Dom;
14399
14400 /**
14401  * @class Roo.dd.DragDrop
14402  * Defines the interface and base operation of items that that can be
14403  * dragged or can be drop targets.  It was designed to be extended, overriding
14404  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14405  * Up to three html elements can be associated with a DragDrop instance:
14406  * <ul>
14407  * <li>linked element: the element that is passed into the constructor.
14408  * This is the element which defines the boundaries for interaction with
14409  * other DragDrop objects.</li>
14410  * <li>handle element(s): The drag operation only occurs if the element that
14411  * was clicked matches a handle element.  By default this is the linked
14412  * element, but there are times that you will want only a portion of the
14413  * linked element to initiate the drag operation, and the setHandleElId()
14414  * method provides a way to define this.</li>
14415  * <li>drag element: this represents the element that would be moved along
14416  * with the cursor during a drag operation.  By default, this is the linked
14417  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14418  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14419  * </li>
14420  * </ul>
14421  * This class should not be instantiated until the onload event to ensure that
14422  * the associated elements are available.
14423  * The following would define a DragDrop obj that would interact with any
14424  * other DragDrop obj in the "group1" group:
14425  * <pre>
14426  *  dd = new Roo.dd.DragDrop("div1", "group1");
14427  * </pre>
14428  * Since none of the event handlers have been implemented, nothing would
14429  * actually happen if you were to run the code above.  Normally you would
14430  * override this class or one of the default implementations, but you can
14431  * also override the methods you want on an instance of the class...
14432  * <pre>
14433  *  dd.onDragDrop = function(e, id) {
14434  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14435  *  }
14436  * </pre>
14437  * @constructor
14438  * @param {String} id of the element that is linked to this instance
14439  * @param {String} sGroup the group of related DragDrop objects
14440  * @param {object} config an object containing configurable attributes
14441  *                Valid properties for DragDrop:
14442  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14443  */
14444 Roo.dd.DragDrop = function(id, sGroup, config) {
14445     if (id) {
14446         this.init(id, sGroup, config);
14447     }
14448 };
14449
14450 Roo.dd.DragDrop.prototype = {
14451
14452     /**
14453      * The id of the element associated with this object.  This is what we
14454      * refer to as the "linked element" because the size and position of
14455      * this element is used to determine when the drag and drop objects have
14456      * interacted.
14457      * @property id
14458      * @type String
14459      */
14460     id: null,
14461
14462     /**
14463      * Configuration attributes passed into the constructor
14464      * @property config
14465      * @type object
14466      */
14467     config: null,
14468
14469     /**
14470      * The id of the element that will be dragged.  By default this is same
14471      * as the linked element , but could be changed to another element. Ex:
14472      * Roo.dd.DDProxy
14473      * @property dragElId
14474      * @type String
14475      * @private
14476      */
14477     dragElId: null,
14478
14479     /**
14480      * the id of the element that initiates the drag operation.  By default
14481      * this is the linked element, but could be changed to be a child of this
14482      * element.  This lets us do things like only starting the drag when the
14483      * header element within the linked html element is clicked.
14484      * @property handleElId
14485      * @type String
14486      * @private
14487      */
14488     handleElId: null,
14489
14490     /**
14491      * An associative array of HTML tags that will be ignored if clicked.
14492      * @property invalidHandleTypes
14493      * @type {string: string}
14494      */
14495     invalidHandleTypes: null,
14496
14497     /**
14498      * An associative array of ids for elements that will be ignored if clicked
14499      * @property invalidHandleIds
14500      * @type {string: string}
14501      */
14502     invalidHandleIds: null,
14503
14504     /**
14505      * An indexted array of css class names for elements that will be ignored
14506      * if clicked.
14507      * @property invalidHandleClasses
14508      * @type string[]
14509      */
14510     invalidHandleClasses: null,
14511
14512     /**
14513      * The linked element's absolute X position at the time the drag was
14514      * started
14515      * @property startPageX
14516      * @type int
14517      * @private
14518      */
14519     startPageX: 0,
14520
14521     /**
14522      * The linked element's absolute X position at the time the drag was
14523      * started
14524      * @property startPageY
14525      * @type int
14526      * @private
14527      */
14528     startPageY: 0,
14529
14530     /**
14531      * The group defines a logical collection of DragDrop objects that are
14532      * related.  Instances only get events when interacting with other
14533      * DragDrop object in the same group.  This lets us define multiple
14534      * groups using a single DragDrop subclass if we want.
14535      * @property groups
14536      * @type {string: string}
14537      */
14538     groups: null,
14539
14540     /**
14541      * Individual drag/drop instances can be locked.  This will prevent
14542      * onmousedown start drag.
14543      * @property locked
14544      * @type boolean
14545      * @private
14546      */
14547     locked: false,
14548
14549     /**
14550      * Lock this instance
14551      * @method lock
14552      */
14553     lock: function() { this.locked = true; },
14554
14555     /**
14556      * Unlock this instace
14557      * @method unlock
14558      */
14559     unlock: function() { this.locked = false; },
14560
14561     /**
14562      * By default, all insances can be a drop target.  This can be disabled by
14563      * setting isTarget to false.
14564      * @method isTarget
14565      * @type boolean
14566      */
14567     isTarget: true,
14568
14569     /**
14570      * The padding configured for this drag and drop object for calculating
14571      * the drop zone intersection with this object.
14572      * @method padding
14573      * @type int[]
14574      */
14575     padding: null,
14576
14577     /**
14578      * Cached reference to the linked element
14579      * @property _domRef
14580      * @private
14581      */
14582     _domRef: null,
14583
14584     /**
14585      * Internal typeof flag
14586      * @property __ygDragDrop
14587      * @private
14588      */
14589     __ygDragDrop: true,
14590
14591     /**
14592      * Set to true when horizontal contraints are applied
14593      * @property constrainX
14594      * @type boolean
14595      * @private
14596      */
14597     constrainX: false,
14598
14599     /**
14600      * Set to true when vertical contraints are applied
14601      * @property constrainY
14602      * @type boolean
14603      * @private
14604      */
14605     constrainY: false,
14606
14607     /**
14608      * The left constraint
14609      * @property minX
14610      * @type int
14611      * @private
14612      */
14613     minX: 0,
14614
14615     /**
14616      * The right constraint
14617      * @property maxX
14618      * @type int
14619      * @private
14620      */
14621     maxX: 0,
14622
14623     /**
14624      * The up constraint
14625      * @property minY
14626      * @type int
14627      * @type int
14628      * @private
14629      */
14630     minY: 0,
14631
14632     /**
14633      * The down constraint
14634      * @property maxY
14635      * @type int
14636      * @private
14637      */
14638     maxY: 0,
14639
14640     /**
14641      * Maintain offsets when we resetconstraints.  Set to true when you want
14642      * the position of the element relative to its parent to stay the same
14643      * when the page changes
14644      *
14645      * @property maintainOffset
14646      * @type boolean
14647      */
14648     maintainOffset: false,
14649
14650     /**
14651      * Array of pixel locations the element will snap to if we specified a
14652      * horizontal graduation/interval.  This array is generated automatically
14653      * when you define a tick interval.
14654      * @property xTicks
14655      * @type int[]
14656      */
14657     xTicks: null,
14658
14659     /**
14660      * Array of pixel locations the element will snap to if we specified a
14661      * vertical graduation/interval.  This array is generated automatically
14662      * when you define a tick interval.
14663      * @property yTicks
14664      * @type int[]
14665      */
14666     yTicks: null,
14667
14668     /**
14669      * By default the drag and drop instance will only respond to the primary
14670      * button click (left button for a right-handed mouse).  Set to true to
14671      * allow drag and drop to start with any mouse click that is propogated
14672      * by the browser
14673      * @property primaryButtonOnly
14674      * @type boolean
14675      */
14676     primaryButtonOnly: true,
14677
14678     /**
14679      * The availabe property is false until the linked dom element is accessible.
14680      * @property available
14681      * @type boolean
14682      */
14683     available: false,
14684
14685     /**
14686      * By default, drags can only be initiated if the mousedown occurs in the
14687      * region the linked element is.  This is done in part to work around a
14688      * bug in some browsers that mis-report the mousedown if the previous
14689      * mouseup happened outside of the window.  This property is set to true
14690      * if outer handles are defined.
14691      *
14692      * @property hasOuterHandles
14693      * @type boolean
14694      * @default false
14695      */
14696     hasOuterHandles: false,
14697
14698     /**
14699      * Code that executes immediately before the startDrag event
14700      * @method b4StartDrag
14701      * @private
14702      */
14703     b4StartDrag: function(x, y) { },
14704
14705     /**
14706      * Abstract method called after a drag/drop object is clicked
14707      * and the drag or mousedown time thresholds have beeen met.
14708      * @method startDrag
14709      * @param {int} X click location
14710      * @param {int} Y click location
14711      */
14712     startDrag: function(x, y) { /* override this */ },
14713
14714     /**
14715      * Code that executes immediately before the onDrag event
14716      * @method b4Drag
14717      * @private
14718      */
14719     b4Drag: function(e) { },
14720
14721     /**
14722      * Abstract method called during the onMouseMove event while dragging an
14723      * object.
14724      * @method onDrag
14725      * @param {Event} e the mousemove event
14726      */
14727     onDrag: function(e) { /* override this */ },
14728
14729     /**
14730      * Abstract method called when this element fist begins hovering over
14731      * another DragDrop obj
14732      * @method onDragEnter
14733      * @param {Event} e the mousemove event
14734      * @param {String|DragDrop[]} id In POINT mode, the element
14735      * id this is hovering over.  In INTERSECT mode, an array of one or more
14736      * dragdrop items being hovered over.
14737      */
14738     onDragEnter: function(e, id) { /* override this */ },
14739
14740     /**
14741      * Code that executes immediately before the onDragOver event
14742      * @method b4DragOver
14743      * @private
14744      */
14745     b4DragOver: function(e) { },
14746
14747     /**
14748      * Abstract method called when this element is hovering over another
14749      * DragDrop obj
14750      * @method onDragOver
14751      * @param {Event} e the mousemove event
14752      * @param {String|DragDrop[]} id In POINT mode, the element
14753      * id this is hovering over.  In INTERSECT mode, an array of dd items
14754      * being hovered over.
14755      */
14756     onDragOver: function(e, id) { /* override this */ },
14757
14758     /**
14759      * Code that executes immediately before the onDragOut event
14760      * @method b4DragOut
14761      * @private
14762      */
14763     b4DragOut: function(e) { },
14764
14765     /**
14766      * Abstract method called when we are no longer hovering over an element
14767      * @method onDragOut
14768      * @param {Event} e the mousemove event
14769      * @param {String|DragDrop[]} id In POINT mode, the element
14770      * id this was hovering over.  In INTERSECT mode, an array of dd items
14771      * that the mouse is no longer over.
14772      */
14773     onDragOut: function(e, id) { /* override this */ },
14774
14775     /**
14776      * Code that executes immediately before the onDragDrop event
14777      * @method b4DragDrop
14778      * @private
14779      */
14780     b4DragDrop: function(e) { },
14781
14782     /**
14783      * Abstract method called when this item is dropped on another DragDrop
14784      * obj
14785      * @method onDragDrop
14786      * @param {Event} e the mouseup event
14787      * @param {String|DragDrop[]} id In POINT mode, the element
14788      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14789      * was dropped on.
14790      */
14791     onDragDrop: function(e, id) { /* override this */ },
14792
14793     /**
14794      * Abstract method called when this item is dropped on an area with no
14795      * drop target
14796      * @method onInvalidDrop
14797      * @param {Event} e the mouseup event
14798      */
14799     onInvalidDrop: function(e) { /* override this */ },
14800
14801     /**
14802      * Code that executes immediately before the endDrag event
14803      * @method b4EndDrag
14804      * @private
14805      */
14806     b4EndDrag: function(e) { },
14807
14808     /**
14809      * Fired when we are done dragging the object
14810      * @method endDrag
14811      * @param {Event} e the mouseup event
14812      */
14813     endDrag: function(e) { /* override this */ },
14814
14815     /**
14816      * Code executed immediately before the onMouseDown event
14817      * @method b4MouseDown
14818      * @param {Event} e the mousedown event
14819      * @private
14820      */
14821     b4MouseDown: function(e) {  },
14822
14823     /**
14824      * Event handler that fires when a drag/drop obj gets a mousedown
14825      * @method onMouseDown
14826      * @param {Event} e the mousedown event
14827      */
14828     onMouseDown: function(e) { /* override this */ },
14829
14830     /**
14831      * Event handler that fires when a drag/drop obj gets a mouseup
14832      * @method onMouseUp
14833      * @param {Event} e the mouseup event
14834      */
14835     onMouseUp: function(e) { /* override this */ },
14836
14837     /**
14838      * Override the onAvailable method to do what is needed after the initial
14839      * position was determined.
14840      * @method onAvailable
14841      */
14842     onAvailable: function () {
14843     },
14844
14845     /*
14846      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14847      * @type Object
14848      */
14849     defaultPadding : {left:0, right:0, top:0, bottom:0},
14850
14851     /*
14852      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14853  *
14854  * Usage:
14855  <pre><code>
14856  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14857                 { dragElId: "existingProxyDiv" });
14858  dd.startDrag = function(){
14859      this.constrainTo("parent-id");
14860  };
14861  </code></pre>
14862  * Or you can initalize it using the {@link Roo.Element} object:
14863  <pre><code>
14864  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14865      startDrag : function(){
14866          this.constrainTo("parent-id");
14867      }
14868  });
14869  </code></pre>
14870      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14871      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14872      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14873      * an object containing the sides to pad. For example: {right:10, bottom:10}
14874      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14875      */
14876     constrainTo : function(constrainTo, pad, inContent){
14877         if(typeof pad == "number"){
14878             pad = {left: pad, right:pad, top:pad, bottom:pad};
14879         }
14880         pad = pad || this.defaultPadding;
14881         var b = Roo.get(this.getEl()).getBox();
14882         var ce = Roo.get(constrainTo);
14883         var s = ce.getScroll();
14884         var c, cd = ce.dom;
14885         if(cd == document.body){
14886             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14887         }else{
14888             xy = ce.getXY();
14889             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14890         }
14891
14892
14893         var topSpace = b.y - c.y;
14894         var leftSpace = b.x - c.x;
14895
14896         this.resetConstraints();
14897         this.setXConstraint(leftSpace - (pad.left||0), // left
14898                 c.width - leftSpace - b.width - (pad.right||0) //right
14899         );
14900         this.setYConstraint(topSpace - (pad.top||0), //top
14901                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14902         );
14903     },
14904
14905     /**
14906      * Returns a reference to the linked element
14907      * @method getEl
14908      * @return {HTMLElement} the html element
14909      */
14910     getEl: function() {
14911         if (!this._domRef) {
14912             this._domRef = Roo.getDom(this.id);
14913         }
14914
14915         return this._domRef;
14916     },
14917
14918     /**
14919      * Returns a reference to the actual element to drag.  By default this is
14920      * the same as the html element, but it can be assigned to another
14921      * element. An example of this can be found in Roo.dd.DDProxy
14922      * @method getDragEl
14923      * @return {HTMLElement} the html element
14924      */
14925     getDragEl: function() {
14926         return Roo.getDom(this.dragElId);
14927     },
14928
14929     /**
14930      * Sets up the DragDrop object.  Must be called in the constructor of any
14931      * Roo.dd.DragDrop subclass
14932      * @method init
14933      * @param id the id of the linked element
14934      * @param {String} sGroup the group of related items
14935      * @param {object} config configuration attributes
14936      */
14937     init: function(id, sGroup, config) {
14938         this.initTarget(id, sGroup, config);
14939         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14940         // Event.on(this.id, "selectstart", Event.preventDefault);
14941     },
14942
14943     /**
14944      * Initializes Targeting functionality only... the object does not
14945      * get a mousedown handler.
14946      * @method initTarget
14947      * @param id the id of the linked element
14948      * @param {String} sGroup the group of related items
14949      * @param {object} config configuration attributes
14950      */
14951     initTarget: function(id, sGroup, config) {
14952
14953         // configuration attributes
14954         this.config = config || {};
14955
14956         // create a local reference to the drag and drop manager
14957         this.DDM = Roo.dd.DDM;
14958         // initialize the groups array
14959         this.groups = {};
14960
14961         // assume that we have an element reference instead of an id if the
14962         // parameter is not a string
14963         if (typeof id !== "string") {
14964             id = Roo.id(id);
14965         }
14966
14967         // set the id
14968         this.id = id;
14969
14970         // add to an interaction group
14971         this.addToGroup((sGroup) ? sGroup : "default");
14972
14973         // We don't want to register this as the handle with the manager
14974         // so we just set the id rather than calling the setter.
14975         this.handleElId = id;
14976
14977         // the linked element is the element that gets dragged by default
14978         this.setDragElId(id);
14979
14980         // by default, clicked anchors will not start drag operations.
14981         this.invalidHandleTypes = { A: "A" };
14982         this.invalidHandleIds = {};
14983         this.invalidHandleClasses = [];
14984
14985         this.applyConfig();
14986
14987         this.handleOnAvailable();
14988     },
14989
14990     /**
14991      * Applies the configuration parameters that were passed into the constructor.
14992      * This is supposed to happen at each level through the inheritance chain.  So
14993      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14994      * DragDrop in order to get all of the parameters that are available in
14995      * each object.
14996      * @method applyConfig
14997      */
14998     applyConfig: function() {
14999
15000         // configurable properties:
15001         //    padding, isTarget, maintainOffset, primaryButtonOnly
15002         this.padding           = this.config.padding || [0, 0, 0, 0];
15003         this.isTarget          = (this.config.isTarget !== false);
15004         this.maintainOffset    = (this.config.maintainOffset);
15005         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15006
15007     },
15008
15009     /**
15010      * Executed when the linked element is available
15011      * @method handleOnAvailable
15012      * @private
15013      */
15014     handleOnAvailable: function() {
15015         this.available = true;
15016         this.resetConstraints();
15017         this.onAvailable();
15018     },
15019
15020      /**
15021      * Configures the padding for the target zone in px.  Effectively expands
15022      * (or reduces) the virtual object size for targeting calculations.
15023      * Supports css-style shorthand; if only one parameter is passed, all sides
15024      * will have that padding, and if only two are passed, the top and bottom
15025      * will have the first param, the left and right the second.
15026      * @method setPadding
15027      * @param {int} iTop    Top pad
15028      * @param {int} iRight  Right pad
15029      * @param {int} iBot    Bot pad
15030      * @param {int} iLeft   Left pad
15031      */
15032     setPadding: function(iTop, iRight, iBot, iLeft) {
15033         // this.padding = [iLeft, iRight, iTop, iBot];
15034         if (!iRight && 0 !== iRight) {
15035             this.padding = [iTop, iTop, iTop, iTop];
15036         } else if (!iBot && 0 !== iBot) {
15037             this.padding = [iTop, iRight, iTop, iRight];
15038         } else {
15039             this.padding = [iTop, iRight, iBot, iLeft];
15040         }
15041     },
15042
15043     /**
15044      * Stores the initial placement of the linked element.
15045      * @method setInitialPosition
15046      * @param {int} diffX   the X offset, default 0
15047      * @param {int} diffY   the Y offset, default 0
15048      */
15049     setInitPosition: function(diffX, diffY) {
15050         var el = this.getEl();
15051
15052         if (!this.DDM.verifyEl(el)) {
15053             return;
15054         }
15055
15056         var dx = diffX || 0;
15057         var dy = diffY || 0;
15058
15059         var p = Dom.getXY( el );
15060
15061         this.initPageX = p[0] - dx;
15062         this.initPageY = p[1] - dy;
15063
15064         this.lastPageX = p[0];
15065         this.lastPageY = p[1];
15066
15067
15068         this.setStartPosition(p);
15069     },
15070
15071     /**
15072      * Sets the start position of the element.  This is set when the obj
15073      * is initialized, the reset when a drag is started.
15074      * @method setStartPosition
15075      * @param pos current position (from previous lookup)
15076      * @private
15077      */
15078     setStartPosition: function(pos) {
15079         var p = pos || Dom.getXY( this.getEl() );
15080         this.deltaSetXY = null;
15081
15082         this.startPageX = p[0];
15083         this.startPageY = p[1];
15084     },
15085
15086     /**
15087      * Add this instance to a group of related drag/drop objects.  All
15088      * instances belong to at least one group, and can belong to as many
15089      * groups as needed.
15090      * @method addToGroup
15091      * @param sGroup {string} the name of the group
15092      */
15093     addToGroup: function(sGroup) {
15094         this.groups[sGroup] = true;
15095         this.DDM.regDragDrop(this, sGroup);
15096     },
15097
15098     /**
15099      * Remove's this instance from the supplied interaction group
15100      * @method removeFromGroup
15101      * @param {string}  sGroup  The group to drop
15102      */
15103     removeFromGroup: function(sGroup) {
15104         if (this.groups[sGroup]) {
15105             delete this.groups[sGroup];
15106         }
15107
15108         this.DDM.removeDDFromGroup(this, sGroup);
15109     },
15110
15111     /**
15112      * Allows you to specify that an element other than the linked element
15113      * will be moved with the cursor during a drag
15114      * @method setDragElId
15115      * @param id {string} the id of the element that will be used to initiate the drag
15116      */
15117     setDragElId: function(id) {
15118         this.dragElId = id;
15119     },
15120
15121     /**
15122      * Allows you to specify a child of the linked element that should be
15123      * used to initiate the drag operation.  An example of this would be if
15124      * you have a content div with text and links.  Clicking anywhere in the
15125      * content area would normally start the drag operation.  Use this method
15126      * to specify that an element inside of the content div is the element
15127      * that starts the drag operation.
15128      * @method setHandleElId
15129      * @param id {string} the id of the element that will be used to
15130      * initiate the drag.
15131      */
15132     setHandleElId: function(id) {
15133         if (typeof id !== "string") {
15134             id = Roo.id(id);
15135         }
15136         this.handleElId = id;
15137         this.DDM.regHandle(this.id, id);
15138     },
15139
15140     /**
15141      * Allows you to set an element outside of the linked element as a drag
15142      * handle
15143      * @method setOuterHandleElId
15144      * @param id the id of the element that will be used to initiate the drag
15145      */
15146     setOuterHandleElId: function(id) {
15147         if (typeof id !== "string") {
15148             id = Roo.id(id);
15149         }
15150         Event.on(id, "mousedown",
15151                 this.handleMouseDown, this);
15152         this.setHandleElId(id);
15153
15154         this.hasOuterHandles = true;
15155     },
15156
15157     /**
15158      * Remove all drag and drop hooks for this element
15159      * @method unreg
15160      */
15161     unreg: function() {
15162         Event.un(this.id, "mousedown",
15163                 this.handleMouseDown);
15164         this._domRef = null;
15165         this.DDM._remove(this);
15166     },
15167
15168     destroy : function(){
15169         this.unreg();
15170     },
15171
15172     /**
15173      * Returns true if this instance is locked, or the drag drop mgr is locked
15174      * (meaning that all drag/drop is disabled on the page.)
15175      * @method isLocked
15176      * @return {boolean} true if this obj or all drag/drop is locked, else
15177      * false
15178      */
15179     isLocked: function() {
15180         return (this.DDM.isLocked() || this.locked);
15181     },
15182
15183     /**
15184      * Fired when this object is clicked
15185      * @method handleMouseDown
15186      * @param {Event} e
15187      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15188      * @private
15189      */
15190     handleMouseDown: function(e, oDD){
15191         if (this.primaryButtonOnly && e.button != 0) {
15192             return;
15193         }
15194
15195         if (this.isLocked()) {
15196             return;
15197         }
15198
15199         this.DDM.refreshCache(this.groups);
15200
15201         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15202         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15203         } else {
15204             if (this.clickValidator(e)) {
15205
15206                 // set the initial element position
15207                 this.setStartPosition();
15208
15209
15210                 this.b4MouseDown(e);
15211                 this.onMouseDown(e);
15212
15213                 this.DDM.handleMouseDown(e, this);
15214
15215                 this.DDM.stopEvent(e);
15216             } else {
15217
15218
15219             }
15220         }
15221     },
15222
15223     clickValidator: function(e) {
15224         var target = e.getTarget();
15225         return ( this.isValidHandleChild(target) &&
15226                     (this.id == this.handleElId ||
15227                         this.DDM.handleWasClicked(target, this.id)) );
15228     },
15229
15230     /**
15231      * Allows you to specify a tag name that should not start a drag operation
15232      * when clicked.  This is designed to facilitate embedding links within a
15233      * drag handle that do something other than start the drag.
15234      * @method addInvalidHandleType
15235      * @param {string} tagName the type of element to exclude
15236      */
15237     addInvalidHandleType: function(tagName) {
15238         var type = tagName.toUpperCase();
15239         this.invalidHandleTypes[type] = type;
15240     },
15241
15242     /**
15243      * Lets you to specify an element id for a child of a drag handle
15244      * that should not initiate a drag
15245      * @method addInvalidHandleId
15246      * @param {string} id the element id of the element you wish to ignore
15247      */
15248     addInvalidHandleId: function(id) {
15249         if (typeof id !== "string") {
15250             id = Roo.id(id);
15251         }
15252         this.invalidHandleIds[id] = id;
15253     },
15254
15255     /**
15256      * Lets you specify a css class of elements that will not initiate a drag
15257      * @method addInvalidHandleClass
15258      * @param {string} cssClass the class of the elements you wish to ignore
15259      */
15260     addInvalidHandleClass: function(cssClass) {
15261         this.invalidHandleClasses.push(cssClass);
15262     },
15263
15264     /**
15265      * Unsets an excluded tag name set by addInvalidHandleType
15266      * @method removeInvalidHandleType
15267      * @param {string} tagName the type of element to unexclude
15268      */
15269     removeInvalidHandleType: function(tagName) {
15270         var type = tagName.toUpperCase();
15271         // this.invalidHandleTypes[type] = null;
15272         delete this.invalidHandleTypes[type];
15273     },
15274
15275     /**
15276      * Unsets an invalid handle id
15277      * @method removeInvalidHandleId
15278      * @param {string} id the id of the element to re-enable
15279      */
15280     removeInvalidHandleId: function(id) {
15281         if (typeof id !== "string") {
15282             id = Roo.id(id);
15283         }
15284         delete this.invalidHandleIds[id];
15285     },
15286
15287     /**
15288      * Unsets an invalid css class
15289      * @method removeInvalidHandleClass
15290      * @param {string} cssClass the class of the element(s) you wish to
15291      * re-enable
15292      */
15293     removeInvalidHandleClass: function(cssClass) {
15294         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15295             if (this.invalidHandleClasses[i] == cssClass) {
15296                 delete this.invalidHandleClasses[i];
15297             }
15298         }
15299     },
15300
15301     /**
15302      * Checks the tag exclusion list to see if this click should be ignored
15303      * @method isValidHandleChild
15304      * @param {HTMLElement} node the HTMLElement to evaluate
15305      * @return {boolean} true if this is a valid tag type, false if not
15306      */
15307     isValidHandleChild: function(node) {
15308
15309         var valid = true;
15310         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15311         var nodeName;
15312         try {
15313             nodeName = node.nodeName.toUpperCase();
15314         } catch(e) {
15315             nodeName = node.nodeName;
15316         }
15317         valid = valid && !this.invalidHandleTypes[nodeName];
15318         valid = valid && !this.invalidHandleIds[node.id];
15319
15320         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15321             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15322         }
15323
15324
15325         return valid;
15326
15327     },
15328
15329     /**
15330      * Create the array of horizontal tick marks if an interval was specified
15331      * in setXConstraint().
15332      * @method setXTicks
15333      * @private
15334      */
15335     setXTicks: function(iStartX, iTickSize) {
15336         this.xTicks = [];
15337         this.xTickSize = iTickSize;
15338
15339         var tickMap = {};
15340
15341         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15342             if (!tickMap[i]) {
15343                 this.xTicks[this.xTicks.length] = i;
15344                 tickMap[i] = true;
15345             }
15346         }
15347
15348         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15349             if (!tickMap[i]) {
15350                 this.xTicks[this.xTicks.length] = i;
15351                 tickMap[i] = true;
15352             }
15353         }
15354
15355         this.xTicks.sort(this.DDM.numericSort) ;
15356     },
15357
15358     /**
15359      * Create the array of vertical tick marks if an interval was specified in
15360      * setYConstraint().
15361      * @method setYTicks
15362      * @private
15363      */
15364     setYTicks: function(iStartY, iTickSize) {
15365         this.yTicks = [];
15366         this.yTickSize = iTickSize;
15367
15368         var tickMap = {};
15369
15370         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15371             if (!tickMap[i]) {
15372                 this.yTicks[this.yTicks.length] = i;
15373                 tickMap[i] = true;
15374             }
15375         }
15376
15377         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15378             if (!tickMap[i]) {
15379                 this.yTicks[this.yTicks.length] = i;
15380                 tickMap[i] = true;
15381             }
15382         }
15383
15384         this.yTicks.sort(this.DDM.numericSort) ;
15385     },
15386
15387     /**
15388      * By default, the element can be dragged any place on the screen.  Use
15389      * this method to limit the horizontal travel of the element.  Pass in
15390      * 0,0 for the parameters if you want to lock the drag to the y axis.
15391      * @method setXConstraint
15392      * @param {int} iLeft the number of pixels the element can move to the left
15393      * @param {int} iRight the number of pixels the element can move to the
15394      * right
15395      * @param {int} iTickSize optional parameter for specifying that the
15396      * element
15397      * should move iTickSize pixels at a time.
15398      */
15399     setXConstraint: function(iLeft, iRight, iTickSize) {
15400         this.leftConstraint = iLeft;
15401         this.rightConstraint = iRight;
15402
15403         this.minX = this.initPageX - iLeft;
15404         this.maxX = this.initPageX + iRight;
15405         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15406
15407         this.constrainX = true;
15408     },
15409
15410     /**
15411      * Clears any constraints applied to this instance.  Also clears ticks
15412      * since they can't exist independent of a constraint at this time.
15413      * @method clearConstraints
15414      */
15415     clearConstraints: function() {
15416         this.constrainX = false;
15417         this.constrainY = false;
15418         this.clearTicks();
15419     },
15420
15421     /**
15422      * Clears any tick interval defined for this instance
15423      * @method clearTicks
15424      */
15425     clearTicks: function() {
15426         this.xTicks = null;
15427         this.yTicks = null;
15428         this.xTickSize = 0;
15429         this.yTickSize = 0;
15430     },
15431
15432     /**
15433      * By default, the element can be dragged any place on the screen.  Set
15434      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15435      * parameters if you want to lock the drag to the x axis.
15436      * @method setYConstraint
15437      * @param {int} iUp the number of pixels the element can move up
15438      * @param {int} iDown the number of pixels the element can move down
15439      * @param {int} iTickSize optional parameter for specifying that the
15440      * element should move iTickSize pixels at a time.
15441      */
15442     setYConstraint: function(iUp, iDown, iTickSize) {
15443         this.topConstraint = iUp;
15444         this.bottomConstraint = iDown;
15445
15446         this.minY = this.initPageY - iUp;
15447         this.maxY = this.initPageY + iDown;
15448         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15449
15450         this.constrainY = true;
15451
15452     },
15453
15454     /**
15455      * resetConstraints must be called if you manually reposition a dd element.
15456      * @method resetConstraints
15457      * @param {boolean} maintainOffset
15458      */
15459     resetConstraints: function() {
15460
15461
15462         // Maintain offsets if necessary
15463         if (this.initPageX || this.initPageX === 0) {
15464             // figure out how much this thing has moved
15465             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15466             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15467
15468             this.setInitPosition(dx, dy);
15469
15470         // This is the first time we have detected the element's position
15471         } else {
15472             this.setInitPosition();
15473         }
15474
15475         if (this.constrainX) {
15476             this.setXConstraint( this.leftConstraint,
15477                                  this.rightConstraint,
15478                                  this.xTickSize        );
15479         }
15480
15481         if (this.constrainY) {
15482             this.setYConstraint( this.topConstraint,
15483                                  this.bottomConstraint,
15484                                  this.yTickSize         );
15485         }
15486     },
15487
15488     /**
15489      * Normally the drag element is moved pixel by pixel, but we can specify
15490      * that it move a number of pixels at a time.  This method resolves the
15491      * location when we have it set up like this.
15492      * @method getTick
15493      * @param {int} val where we want to place the object
15494      * @param {int[]} tickArray sorted array of valid points
15495      * @return {int} the closest tick
15496      * @private
15497      */
15498     getTick: function(val, tickArray) {
15499
15500         if (!tickArray) {
15501             // If tick interval is not defined, it is effectively 1 pixel,
15502             // so we return the value passed to us.
15503             return val;
15504         } else if (tickArray[0] >= val) {
15505             // The value is lower than the first tick, so we return the first
15506             // tick.
15507             return tickArray[0];
15508         } else {
15509             for (var i=0, len=tickArray.length; i<len; ++i) {
15510                 var next = i + 1;
15511                 if (tickArray[next] && tickArray[next] >= val) {
15512                     var diff1 = val - tickArray[i];
15513                     var diff2 = tickArray[next] - val;
15514                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15515                 }
15516             }
15517
15518             // The value is larger than the last tick, so we return the last
15519             // tick.
15520             return tickArray[tickArray.length - 1];
15521         }
15522     },
15523
15524     /**
15525      * toString method
15526      * @method toString
15527      * @return {string} string representation of the dd obj
15528      */
15529     toString: function() {
15530         return ("DragDrop " + this.id);
15531     }
15532
15533 };
15534
15535 })();
15536 /*
15537  * Based on:
15538  * Ext JS Library 1.1.1
15539  * Copyright(c) 2006-2007, Ext JS, LLC.
15540  *
15541  * Originally Released Under LGPL - original licence link has changed is not relivant.
15542  *
15543  * Fork - LGPL
15544  * <script type="text/javascript">
15545  */
15546
15547
15548 /**
15549  * The drag and drop utility provides a framework for building drag and drop
15550  * applications.  In addition to enabling drag and drop for specific elements,
15551  * the drag and drop elements are tracked by the manager class, and the
15552  * interactions between the various elements are tracked during the drag and
15553  * the implementing code is notified about these important moments.
15554  */
15555
15556 // Only load the library once.  Rewriting the manager class would orphan
15557 // existing drag and drop instances.
15558 if (!Roo.dd.DragDropMgr) {
15559
15560 /**
15561  * @class Roo.dd.DragDropMgr
15562  * DragDropMgr is a singleton that tracks the element interaction for
15563  * all DragDrop items in the window.  Generally, you will not call
15564  * this class directly, but it does have helper methods that could
15565  * be useful in your DragDrop implementations.
15566  * @singleton
15567  */
15568 Roo.dd.DragDropMgr = function() {
15569
15570     var Event = Roo.EventManager;
15571
15572     return {
15573
15574         /**
15575          * Two dimensional Array of registered DragDrop objects.  The first
15576          * dimension is the DragDrop item group, the second the DragDrop
15577          * object.
15578          * @property ids
15579          * @type {string: string}
15580          * @private
15581          * @static
15582          */
15583         ids: {},
15584
15585         /**
15586          * Array of element ids defined as drag handles.  Used to determine
15587          * if the element that generated the mousedown event is actually the
15588          * handle and not the html element itself.
15589          * @property handleIds
15590          * @type {string: string}
15591          * @private
15592          * @static
15593          */
15594         handleIds: {},
15595
15596         /**
15597          * the DragDrop object that is currently being dragged
15598          * @property dragCurrent
15599          * @type DragDrop
15600          * @private
15601          * @static
15602          **/
15603         dragCurrent: null,
15604
15605         /**
15606          * the DragDrop object(s) that are being hovered over
15607          * @property dragOvers
15608          * @type Array
15609          * @private
15610          * @static
15611          */
15612         dragOvers: {},
15613
15614         /**
15615          * the X distance between the cursor and the object being dragged
15616          * @property deltaX
15617          * @type int
15618          * @private
15619          * @static
15620          */
15621         deltaX: 0,
15622
15623         /**
15624          * the Y distance between the cursor and the object being dragged
15625          * @property deltaY
15626          * @type int
15627          * @private
15628          * @static
15629          */
15630         deltaY: 0,
15631
15632         /**
15633          * Flag to determine if we should prevent the default behavior of the
15634          * events we define. By default this is true, but this can be set to
15635          * false if you need the default behavior (not recommended)
15636          * @property preventDefault
15637          * @type boolean
15638          * @static
15639          */
15640         preventDefault: true,
15641
15642         /**
15643          * Flag to determine if we should stop the propagation of the events
15644          * we generate. This is true by default but you may want to set it to
15645          * false if the html element contains other features that require the
15646          * mouse click.
15647          * @property stopPropagation
15648          * @type boolean
15649          * @static
15650          */
15651         stopPropagation: true,
15652
15653         /**
15654          * Internal flag that is set to true when drag and drop has been
15655          * intialized
15656          * @property initialized
15657          * @private
15658          * @static
15659          */
15660         initalized: false,
15661
15662         /**
15663          * All drag and drop can be disabled.
15664          * @property locked
15665          * @private
15666          * @static
15667          */
15668         locked: false,
15669
15670         /**
15671          * Called the first time an element is registered.
15672          * @method init
15673          * @private
15674          * @static
15675          */
15676         init: function() {
15677             this.initialized = true;
15678         },
15679
15680         /**
15681          * In point mode, drag and drop interaction is defined by the
15682          * location of the cursor during the drag/drop
15683          * @property POINT
15684          * @type int
15685          * @static
15686          */
15687         POINT: 0,
15688
15689         /**
15690          * In intersect mode, drag and drop interactio nis defined by the
15691          * overlap of two or more drag and drop objects.
15692          * @property INTERSECT
15693          * @type int
15694          * @static
15695          */
15696         INTERSECT: 1,
15697
15698         /**
15699          * The current drag and drop mode.  Default: POINT
15700          * @property mode
15701          * @type int
15702          * @static
15703          */
15704         mode: 0,
15705
15706         /**
15707          * Runs method on all drag and drop objects
15708          * @method _execOnAll
15709          * @private
15710          * @static
15711          */
15712         _execOnAll: function(sMethod, args) {
15713             for (var i in this.ids) {
15714                 for (var j in this.ids[i]) {
15715                     var oDD = this.ids[i][j];
15716                     if (! this.isTypeOfDD(oDD)) {
15717                         continue;
15718                     }
15719                     oDD[sMethod].apply(oDD, args);
15720                 }
15721             }
15722         },
15723
15724         /**
15725          * Drag and drop initialization.  Sets up the global event handlers
15726          * @method _onLoad
15727          * @private
15728          * @static
15729          */
15730         _onLoad: function() {
15731
15732             this.init();
15733
15734
15735             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15736             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15737             Event.on(window,   "unload",    this._onUnload, this, true);
15738             Event.on(window,   "resize",    this._onResize, this, true);
15739             // Event.on(window,   "mouseout",    this._test);
15740
15741         },
15742
15743         /**
15744          * Reset constraints on all drag and drop objs
15745          * @method _onResize
15746          * @private
15747          * @static
15748          */
15749         _onResize: function(e) {
15750             this._execOnAll("resetConstraints", []);
15751         },
15752
15753         /**
15754          * Lock all drag and drop functionality
15755          * @method lock
15756          * @static
15757          */
15758         lock: function() { this.locked = true; },
15759
15760         /**
15761          * Unlock all drag and drop functionality
15762          * @method unlock
15763          * @static
15764          */
15765         unlock: function() { this.locked = false; },
15766
15767         /**
15768          * Is drag and drop locked?
15769          * @method isLocked
15770          * @return {boolean} True if drag and drop is locked, false otherwise.
15771          * @static
15772          */
15773         isLocked: function() { return this.locked; },
15774
15775         /**
15776          * Location cache that is set for all drag drop objects when a drag is
15777          * initiated, cleared when the drag is finished.
15778          * @property locationCache
15779          * @private
15780          * @static
15781          */
15782         locationCache: {},
15783
15784         /**
15785          * Set useCache to false if you want to force object the lookup of each
15786          * drag and drop linked element constantly during a drag.
15787          * @property useCache
15788          * @type boolean
15789          * @static
15790          */
15791         useCache: true,
15792
15793         /**
15794          * The number of pixels that the mouse needs to move after the
15795          * mousedown before the drag is initiated.  Default=3;
15796          * @property clickPixelThresh
15797          * @type int
15798          * @static
15799          */
15800         clickPixelThresh: 3,
15801
15802         /**
15803          * The number of milliseconds after the mousedown event to initiate the
15804          * drag if we don't get a mouseup event. Default=1000
15805          * @property clickTimeThresh
15806          * @type int
15807          * @static
15808          */
15809         clickTimeThresh: 350,
15810
15811         /**
15812          * Flag that indicates that either the drag pixel threshold or the
15813          * mousdown time threshold has been met
15814          * @property dragThreshMet
15815          * @type boolean
15816          * @private
15817          * @static
15818          */
15819         dragThreshMet: false,
15820
15821         /**
15822          * Timeout used for the click time threshold
15823          * @property clickTimeout
15824          * @type Object
15825          * @private
15826          * @static
15827          */
15828         clickTimeout: null,
15829
15830         /**
15831          * The X position of the mousedown event stored for later use when a
15832          * drag threshold is met.
15833          * @property startX
15834          * @type int
15835          * @private
15836          * @static
15837          */
15838         startX: 0,
15839
15840         /**
15841          * The Y position of the mousedown event stored for later use when a
15842          * drag threshold is met.
15843          * @property startY
15844          * @type int
15845          * @private
15846          * @static
15847          */
15848         startY: 0,
15849
15850         /**
15851          * Each DragDrop instance must be registered with the DragDropMgr.
15852          * This is executed in DragDrop.init()
15853          * @method regDragDrop
15854          * @param {DragDrop} oDD the DragDrop object to register
15855          * @param {String} sGroup the name of the group this element belongs to
15856          * @static
15857          */
15858         regDragDrop: function(oDD, sGroup) {
15859             if (!this.initialized) { this.init(); }
15860
15861             if (!this.ids[sGroup]) {
15862                 this.ids[sGroup] = {};
15863             }
15864             this.ids[sGroup][oDD.id] = oDD;
15865         },
15866
15867         /**
15868          * Removes the supplied dd instance from the supplied group. Executed
15869          * by DragDrop.removeFromGroup, so don't call this function directly.
15870          * @method removeDDFromGroup
15871          * @private
15872          * @static
15873          */
15874         removeDDFromGroup: function(oDD, sGroup) {
15875             if (!this.ids[sGroup]) {
15876                 this.ids[sGroup] = {};
15877             }
15878
15879             var obj = this.ids[sGroup];
15880             if (obj && obj[oDD.id]) {
15881                 delete obj[oDD.id];
15882             }
15883         },
15884
15885         /**
15886          * Unregisters a drag and drop item.  This is executed in
15887          * DragDrop.unreg, use that method instead of calling this directly.
15888          * @method _remove
15889          * @private
15890          * @static
15891          */
15892         _remove: function(oDD) {
15893             for (var g in oDD.groups) {
15894                 if (g && this.ids[g][oDD.id]) {
15895                     delete this.ids[g][oDD.id];
15896                 }
15897             }
15898             delete this.handleIds[oDD.id];
15899         },
15900
15901         /**
15902          * Each DragDrop handle element must be registered.  This is done
15903          * automatically when executing DragDrop.setHandleElId()
15904          * @method regHandle
15905          * @param {String} sDDId the DragDrop id this element is a handle for
15906          * @param {String} sHandleId the id of the element that is the drag
15907          * handle
15908          * @static
15909          */
15910         regHandle: function(sDDId, sHandleId) {
15911             if (!this.handleIds[sDDId]) {
15912                 this.handleIds[sDDId] = {};
15913             }
15914             this.handleIds[sDDId][sHandleId] = sHandleId;
15915         },
15916
15917         /**
15918          * Utility function to determine if a given element has been
15919          * registered as a drag drop item.
15920          * @method isDragDrop
15921          * @param {String} id the element id to check
15922          * @return {boolean} true if this element is a DragDrop item,
15923          * false otherwise
15924          * @static
15925          */
15926         isDragDrop: function(id) {
15927             return ( this.getDDById(id) ) ? true : false;
15928         },
15929
15930         /**
15931          * Returns the drag and drop instances that are in all groups the
15932          * passed in instance belongs to.
15933          * @method getRelated
15934          * @param {DragDrop} p_oDD the obj to get related data for
15935          * @param {boolean} bTargetsOnly if true, only return targetable objs
15936          * @return {DragDrop[]} the related instances
15937          * @static
15938          */
15939         getRelated: function(p_oDD, bTargetsOnly) {
15940             var oDDs = [];
15941             for (var i in p_oDD.groups) {
15942                 for (j in this.ids[i]) {
15943                     var dd = this.ids[i][j];
15944                     if (! this.isTypeOfDD(dd)) {
15945                         continue;
15946                     }
15947                     if (!bTargetsOnly || dd.isTarget) {
15948                         oDDs[oDDs.length] = dd;
15949                     }
15950                 }
15951             }
15952
15953             return oDDs;
15954         },
15955
15956         /**
15957          * Returns true if the specified dd target is a legal target for
15958          * the specifice drag obj
15959          * @method isLegalTarget
15960          * @param {DragDrop} the drag obj
15961          * @param {DragDrop} the target
15962          * @return {boolean} true if the target is a legal target for the
15963          * dd obj
15964          * @static
15965          */
15966         isLegalTarget: function (oDD, oTargetDD) {
15967             var targets = this.getRelated(oDD, true);
15968             for (var i=0, len=targets.length;i<len;++i) {
15969                 if (targets[i].id == oTargetDD.id) {
15970                     return true;
15971                 }
15972             }
15973
15974             return false;
15975         },
15976
15977         /**
15978          * My goal is to be able to transparently determine if an object is
15979          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15980          * returns "object", oDD.constructor.toString() always returns
15981          * "DragDrop" and not the name of the subclass.  So for now it just
15982          * evaluates a well-known variable in DragDrop.
15983          * @method isTypeOfDD
15984          * @param {Object} the object to evaluate
15985          * @return {boolean} true if typeof oDD = DragDrop
15986          * @static
15987          */
15988         isTypeOfDD: function (oDD) {
15989             return (oDD && oDD.__ygDragDrop);
15990         },
15991
15992         /**
15993          * Utility function to determine if a given element has been
15994          * registered as a drag drop handle for the given Drag Drop object.
15995          * @method isHandle
15996          * @param {String} id the element id to check
15997          * @return {boolean} true if this element is a DragDrop handle, false
15998          * otherwise
15999          * @static
16000          */
16001         isHandle: function(sDDId, sHandleId) {
16002             return ( this.handleIds[sDDId] &&
16003                             this.handleIds[sDDId][sHandleId] );
16004         },
16005
16006         /**
16007          * Returns the DragDrop instance for a given id
16008          * @method getDDById
16009          * @param {String} id the id of the DragDrop object
16010          * @return {DragDrop} the drag drop object, null if it is not found
16011          * @static
16012          */
16013         getDDById: function(id) {
16014             for (var i in this.ids) {
16015                 if (this.ids[i][id]) {
16016                     return this.ids[i][id];
16017                 }
16018             }
16019             return null;
16020         },
16021
16022         /**
16023          * Fired after a registered DragDrop object gets the mousedown event.
16024          * Sets up the events required to track the object being dragged
16025          * @method handleMouseDown
16026          * @param {Event} e the event
16027          * @param oDD the DragDrop object being dragged
16028          * @private
16029          * @static
16030          */
16031         handleMouseDown: function(e, oDD) {
16032             if(Roo.QuickTips){
16033                 Roo.QuickTips.disable();
16034             }
16035             this.currentTarget = e.getTarget();
16036
16037             this.dragCurrent = oDD;
16038
16039             var el = oDD.getEl();
16040
16041             // track start position
16042             this.startX = e.getPageX();
16043             this.startY = e.getPageY();
16044
16045             this.deltaX = this.startX - el.offsetLeft;
16046             this.deltaY = this.startY - el.offsetTop;
16047
16048             this.dragThreshMet = false;
16049
16050             this.clickTimeout = setTimeout(
16051                     function() {
16052                         var DDM = Roo.dd.DDM;
16053                         DDM.startDrag(DDM.startX, DDM.startY);
16054                     },
16055                     this.clickTimeThresh );
16056         },
16057
16058         /**
16059          * Fired when either the drag pixel threshol or the mousedown hold
16060          * time threshold has been met.
16061          * @method startDrag
16062          * @param x {int} the X position of the original mousedown
16063          * @param y {int} the Y position of the original mousedown
16064          * @static
16065          */
16066         startDrag: function(x, y) {
16067             clearTimeout(this.clickTimeout);
16068             if (this.dragCurrent) {
16069                 this.dragCurrent.b4StartDrag(x, y);
16070                 this.dragCurrent.startDrag(x, y);
16071             }
16072             this.dragThreshMet = true;
16073         },
16074
16075         /**
16076          * Internal function to handle the mouseup event.  Will be invoked
16077          * from the context of the document.
16078          * @method handleMouseUp
16079          * @param {Event} e the event
16080          * @private
16081          * @static
16082          */
16083         handleMouseUp: function(e) {
16084
16085             if(Roo.QuickTips){
16086                 Roo.QuickTips.enable();
16087             }
16088             if (! this.dragCurrent) {
16089                 return;
16090             }
16091
16092             clearTimeout(this.clickTimeout);
16093
16094             if (this.dragThreshMet) {
16095                 this.fireEvents(e, true);
16096             } else {
16097             }
16098
16099             this.stopDrag(e);
16100
16101             this.stopEvent(e);
16102         },
16103
16104         /**
16105          * Utility to stop event propagation and event default, if these
16106          * features are turned on.
16107          * @method stopEvent
16108          * @param {Event} e the event as returned by this.getEvent()
16109          * @static
16110          */
16111         stopEvent: function(e){
16112             if(this.stopPropagation) {
16113                 e.stopPropagation();
16114             }
16115
16116             if (this.preventDefault) {
16117                 e.preventDefault();
16118             }
16119         },
16120
16121         /**
16122          * Internal function to clean up event handlers after the drag
16123          * operation is complete
16124          * @method stopDrag
16125          * @param {Event} e the event
16126          * @private
16127          * @static
16128          */
16129         stopDrag: function(e) {
16130             // Fire the drag end event for the item that was dragged
16131             if (this.dragCurrent) {
16132                 if (this.dragThreshMet) {
16133                     this.dragCurrent.b4EndDrag(e);
16134                     this.dragCurrent.endDrag(e);
16135                 }
16136
16137                 this.dragCurrent.onMouseUp(e);
16138             }
16139
16140             this.dragCurrent = null;
16141             this.dragOvers = {};
16142         },
16143
16144         /**
16145          * Internal function to handle the mousemove event.  Will be invoked
16146          * from the context of the html element.
16147          *
16148          * @TODO figure out what we can do about mouse events lost when the
16149          * user drags objects beyond the window boundary.  Currently we can
16150          * detect this in internet explorer by verifying that the mouse is
16151          * down during the mousemove event.  Firefox doesn't give us the
16152          * button state on the mousemove event.
16153          * @method handleMouseMove
16154          * @param {Event} e the event
16155          * @private
16156          * @static
16157          */
16158         handleMouseMove: function(e) {
16159             if (! this.dragCurrent) {
16160                 return true;
16161             }
16162
16163             // var button = e.which || e.button;
16164
16165             // check for IE mouseup outside of page boundary
16166             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16167                 this.stopEvent(e);
16168                 return this.handleMouseUp(e);
16169             }
16170
16171             if (!this.dragThreshMet) {
16172                 var diffX = Math.abs(this.startX - e.getPageX());
16173                 var diffY = Math.abs(this.startY - e.getPageY());
16174                 if (diffX > this.clickPixelThresh ||
16175                             diffY > this.clickPixelThresh) {
16176                     this.startDrag(this.startX, this.startY);
16177                 }
16178             }
16179
16180             if (this.dragThreshMet) {
16181                 this.dragCurrent.b4Drag(e);
16182                 this.dragCurrent.onDrag(e);
16183                 if(!this.dragCurrent.moveOnly){
16184                     this.fireEvents(e, false);
16185                 }
16186             }
16187
16188             this.stopEvent(e);
16189
16190             return true;
16191         },
16192
16193         /**
16194          * Iterates over all of the DragDrop elements to find ones we are
16195          * hovering over or dropping on
16196          * @method fireEvents
16197          * @param {Event} e the event
16198          * @param {boolean} isDrop is this a drop op or a mouseover op?
16199          * @private
16200          * @static
16201          */
16202         fireEvents: function(e, isDrop) {
16203             var dc = this.dragCurrent;
16204
16205             // If the user did the mouse up outside of the window, we could
16206             // get here even though we have ended the drag.
16207             if (!dc || dc.isLocked()) {
16208                 return;
16209             }
16210
16211             var pt = e.getPoint();
16212
16213             // cache the previous dragOver array
16214             var oldOvers = [];
16215
16216             var outEvts   = [];
16217             var overEvts  = [];
16218             var dropEvts  = [];
16219             var enterEvts = [];
16220
16221             // Check to see if the object(s) we were hovering over is no longer
16222             // being hovered over so we can fire the onDragOut event
16223             for (var i in this.dragOvers) {
16224
16225                 var ddo = this.dragOvers[i];
16226
16227                 if (! this.isTypeOfDD(ddo)) {
16228                     continue;
16229                 }
16230
16231                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16232                     outEvts.push( ddo );
16233                 }
16234
16235                 oldOvers[i] = true;
16236                 delete this.dragOvers[i];
16237             }
16238
16239             for (var sGroup in dc.groups) {
16240
16241                 if ("string" != typeof sGroup) {
16242                     continue;
16243                 }
16244
16245                 for (i in this.ids[sGroup]) {
16246                     var oDD = this.ids[sGroup][i];
16247                     if (! this.isTypeOfDD(oDD)) {
16248                         continue;
16249                     }
16250
16251                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16252                         if (this.isOverTarget(pt, oDD, this.mode)) {
16253                             // look for drop interactions
16254                             if (isDrop) {
16255                                 dropEvts.push( oDD );
16256                             // look for drag enter and drag over interactions
16257                             } else {
16258
16259                                 // initial drag over: dragEnter fires
16260                                 if (!oldOvers[oDD.id]) {
16261                                     enterEvts.push( oDD );
16262                                 // subsequent drag overs: dragOver fires
16263                                 } else {
16264                                     overEvts.push( oDD );
16265                                 }
16266
16267                                 this.dragOvers[oDD.id] = oDD;
16268                             }
16269                         }
16270                     }
16271                 }
16272             }
16273
16274             if (this.mode) {
16275                 if (outEvts.length) {
16276                     dc.b4DragOut(e, outEvts);
16277                     dc.onDragOut(e, outEvts);
16278                 }
16279
16280                 if (enterEvts.length) {
16281                     dc.onDragEnter(e, enterEvts);
16282                 }
16283
16284                 if (overEvts.length) {
16285                     dc.b4DragOver(e, overEvts);
16286                     dc.onDragOver(e, overEvts);
16287                 }
16288
16289                 if (dropEvts.length) {
16290                     dc.b4DragDrop(e, dropEvts);
16291                     dc.onDragDrop(e, dropEvts);
16292                 }
16293
16294             } else {
16295                 // fire dragout events
16296                 var len = 0;
16297                 for (i=0, len=outEvts.length; i<len; ++i) {
16298                     dc.b4DragOut(e, outEvts[i].id);
16299                     dc.onDragOut(e, outEvts[i].id);
16300                 }
16301
16302                 // fire enter events
16303                 for (i=0,len=enterEvts.length; i<len; ++i) {
16304                     // dc.b4DragEnter(e, oDD.id);
16305                     dc.onDragEnter(e, enterEvts[i].id);
16306                 }
16307
16308                 // fire over events
16309                 for (i=0,len=overEvts.length; i<len; ++i) {
16310                     dc.b4DragOver(e, overEvts[i].id);
16311                     dc.onDragOver(e, overEvts[i].id);
16312                 }
16313
16314                 // fire drop events
16315                 for (i=0, len=dropEvts.length; i<len; ++i) {
16316                     dc.b4DragDrop(e, dropEvts[i].id);
16317                     dc.onDragDrop(e, dropEvts[i].id);
16318                 }
16319
16320             }
16321
16322             // notify about a drop that did not find a target
16323             if (isDrop && !dropEvts.length) {
16324                 dc.onInvalidDrop(e);
16325             }
16326
16327         },
16328
16329         /**
16330          * Helper function for getting the best match from the list of drag
16331          * and drop objects returned by the drag and drop events when we are
16332          * in INTERSECT mode.  It returns either the first object that the
16333          * cursor is over, or the object that has the greatest overlap with
16334          * the dragged element.
16335          * @method getBestMatch
16336          * @param  {DragDrop[]} dds The array of drag and drop objects
16337          * targeted
16338          * @return {DragDrop}       The best single match
16339          * @static
16340          */
16341         getBestMatch: function(dds) {
16342             var winner = null;
16343             // Return null if the input is not what we expect
16344             //if (!dds || !dds.length || dds.length == 0) {
16345                // winner = null;
16346             // If there is only one item, it wins
16347             //} else if (dds.length == 1) {
16348
16349             var len = dds.length;
16350
16351             if (len == 1) {
16352                 winner = dds[0];
16353             } else {
16354                 // Loop through the targeted items
16355                 for (var i=0; i<len; ++i) {
16356                     var dd = dds[i];
16357                     // If the cursor is over the object, it wins.  If the
16358                     // cursor is over multiple matches, the first one we come
16359                     // to wins.
16360                     if (dd.cursorIsOver) {
16361                         winner = dd;
16362                         break;
16363                     // Otherwise the object with the most overlap wins
16364                     } else {
16365                         if (!winner ||
16366                             winner.overlap.getArea() < dd.overlap.getArea()) {
16367                             winner = dd;
16368                         }
16369                     }
16370                 }
16371             }
16372
16373             return winner;
16374         },
16375
16376         /**
16377          * Refreshes the cache of the top-left and bottom-right points of the
16378          * drag and drop objects in the specified group(s).  This is in the
16379          * format that is stored in the drag and drop instance, so typical
16380          * usage is:
16381          * <code>
16382          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16383          * </code>
16384          * Alternatively:
16385          * <code>
16386          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16387          * </code>
16388          * @TODO this really should be an indexed array.  Alternatively this
16389          * method could accept both.
16390          * @method refreshCache
16391          * @param {Object} groups an associative array of groups to refresh
16392          * @static
16393          */
16394         refreshCache: function(groups) {
16395             for (var sGroup in groups) {
16396                 if ("string" != typeof sGroup) {
16397                     continue;
16398                 }
16399                 for (var i in this.ids[sGroup]) {
16400                     var oDD = this.ids[sGroup][i];
16401
16402                     if (this.isTypeOfDD(oDD)) {
16403                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16404                         var loc = this.getLocation(oDD);
16405                         if (loc) {
16406                             this.locationCache[oDD.id] = loc;
16407                         } else {
16408                             delete this.locationCache[oDD.id];
16409                             // this will unregister the drag and drop object if
16410                             // the element is not in a usable state
16411                             // oDD.unreg();
16412                         }
16413                     }
16414                 }
16415             }
16416         },
16417
16418         /**
16419          * This checks to make sure an element exists and is in the DOM.  The
16420          * main purpose is to handle cases where innerHTML is used to remove
16421          * drag and drop objects from the DOM.  IE provides an 'unspecified
16422          * error' when trying to access the offsetParent of such an element
16423          * @method verifyEl
16424          * @param {HTMLElement} el the element to check
16425          * @return {boolean} true if the element looks usable
16426          * @static
16427          */
16428         verifyEl: function(el) {
16429             if (el) {
16430                 var parent;
16431                 if(Roo.isIE){
16432                     try{
16433                         parent = el.offsetParent;
16434                     }catch(e){}
16435                 }else{
16436                     parent = el.offsetParent;
16437                 }
16438                 if (parent) {
16439                     return true;
16440                 }
16441             }
16442
16443             return false;
16444         },
16445
16446         /**
16447          * Returns a Region object containing the drag and drop element's position
16448          * and size, including the padding configured for it
16449          * @method getLocation
16450          * @param {DragDrop} oDD the drag and drop object to get the
16451          *                       location for
16452          * @return {Roo.lib.Region} a Region object representing the total area
16453          *                             the element occupies, including any padding
16454          *                             the instance is configured for.
16455          * @static
16456          */
16457         getLocation: function(oDD) {
16458             if (! this.isTypeOfDD(oDD)) {
16459                 return null;
16460             }
16461
16462             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16463
16464             try {
16465                 pos= Roo.lib.Dom.getXY(el);
16466             } catch (e) { }
16467
16468             if (!pos) {
16469                 return null;
16470             }
16471
16472             x1 = pos[0];
16473             x2 = x1 + el.offsetWidth;
16474             y1 = pos[1];
16475             y2 = y1 + el.offsetHeight;
16476
16477             t = y1 - oDD.padding[0];
16478             r = x2 + oDD.padding[1];
16479             b = y2 + oDD.padding[2];
16480             l = x1 - oDD.padding[3];
16481
16482             return new Roo.lib.Region( t, r, b, l );
16483         },
16484
16485         /**
16486          * Checks the cursor location to see if it over the target
16487          * @method isOverTarget
16488          * @param {Roo.lib.Point} pt The point to evaluate
16489          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16490          * @return {boolean} true if the mouse is over the target
16491          * @private
16492          * @static
16493          */
16494         isOverTarget: function(pt, oTarget, intersect) {
16495             // use cache if available
16496             var loc = this.locationCache[oTarget.id];
16497             if (!loc || !this.useCache) {
16498                 loc = this.getLocation(oTarget);
16499                 this.locationCache[oTarget.id] = loc;
16500
16501             }
16502
16503             if (!loc) {
16504                 return false;
16505             }
16506
16507             oTarget.cursorIsOver = loc.contains( pt );
16508
16509             // DragDrop is using this as a sanity check for the initial mousedown
16510             // in this case we are done.  In POINT mode, if the drag obj has no
16511             // contraints, we are also done. Otherwise we need to evaluate the
16512             // location of the target as related to the actual location of the
16513             // dragged element.
16514             var dc = this.dragCurrent;
16515             if (!dc || !dc.getTargetCoord ||
16516                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16517                 return oTarget.cursorIsOver;
16518             }
16519
16520             oTarget.overlap = null;
16521
16522             // Get the current location of the drag element, this is the
16523             // location of the mouse event less the delta that represents
16524             // where the original mousedown happened on the element.  We
16525             // need to consider constraints and ticks as well.
16526             var pos = dc.getTargetCoord(pt.x, pt.y);
16527
16528             var el = dc.getDragEl();
16529             var curRegion = new Roo.lib.Region( pos.y,
16530                                                    pos.x + el.offsetWidth,
16531                                                    pos.y + el.offsetHeight,
16532                                                    pos.x );
16533
16534             var overlap = curRegion.intersect(loc);
16535
16536             if (overlap) {
16537                 oTarget.overlap = overlap;
16538                 return (intersect) ? true : oTarget.cursorIsOver;
16539             } else {
16540                 return false;
16541             }
16542         },
16543
16544         /**
16545          * unload event handler
16546          * @method _onUnload
16547          * @private
16548          * @static
16549          */
16550         _onUnload: function(e, me) {
16551             Roo.dd.DragDropMgr.unregAll();
16552         },
16553
16554         /**
16555          * Cleans up the drag and drop events and objects.
16556          * @method unregAll
16557          * @private
16558          * @static
16559          */
16560         unregAll: function() {
16561
16562             if (this.dragCurrent) {
16563                 this.stopDrag();
16564                 this.dragCurrent = null;
16565             }
16566
16567             this._execOnAll("unreg", []);
16568
16569             for (i in this.elementCache) {
16570                 delete this.elementCache[i];
16571             }
16572
16573             this.elementCache = {};
16574             this.ids = {};
16575         },
16576
16577         /**
16578          * A cache of DOM elements
16579          * @property elementCache
16580          * @private
16581          * @static
16582          */
16583         elementCache: {},
16584
16585         /**
16586          * Get the wrapper for the DOM element specified
16587          * @method getElWrapper
16588          * @param {String} id the id of the element to get
16589          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16590          * @private
16591          * @deprecated This wrapper isn't that useful
16592          * @static
16593          */
16594         getElWrapper: function(id) {
16595             var oWrapper = this.elementCache[id];
16596             if (!oWrapper || !oWrapper.el) {
16597                 oWrapper = this.elementCache[id] =
16598                     new this.ElementWrapper(Roo.getDom(id));
16599             }
16600             return oWrapper;
16601         },
16602
16603         /**
16604          * Returns the actual DOM element
16605          * @method getElement
16606          * @param {String} id the id of the elment to get
16607          * @return {Object} The element
16608          * @deprecated use Roo.getDom instead
16609          * @static
16610          */
16611         getElement: function(id) {
16612             return Roo.getDom(id);
16613         },
16614
16615         /**
16616          * Returns the style property for the DOM element (i.e.,
16617          * document.getElById(id).style)
16618          * @method getCss
16619          * @param {String} id the id of the elment to get
16620          * @return {Object} The style property of the element
16621          * @deprecated use Roo.getDom instead
16622          * @static
16623          */
16624         getCss: function(id) {
16625             var el = Roo.getDom(id);
16626             return (el) ? el.style : null;
16627         },
16628
16629         /**
16630          * Inner class for cached elements
16631          * @class DragDropMgr.ElementWrapper
16632          * @for DragDropMgr
16633          * @private
16634          * @deprecated
16635          */
16636         ElementWrapper: function(el) {
16637                 /**
16638                  * The element
16639                  * @property el
16640                  */
16641                 this.el = el || null;
16642                 /**
16643                  * The element id
16644                  * @property id
16645                  */
16646                 this.id = this.el && el.id;
16647                 /**
16648                  * A reference to the style property
16649                  * @property css
16650                  */
16651                 this.css = this.el && el.style;
16652             },
16653
16654         /**
16655          * Returns the X position of an html element
16656          * @method getPosX
16657          * @param el the element for which to get the position
16658          * @return {int} the X coordinate
16659          * @for DragDropMgr
16660          * @deprecated use Roo.lib.Dom.getX instead
16661          * @static
16662          */
16663         getPosX: function(el) {
16664             return Roo.lib.Dom.getX(el);
16665         },
16666
16667         /**
16668          * Returns the Y position of an html element
16669          * @method getPosY
16670          * @param el the element for which to get the position
16671          * @return {int} the Y coordinate
16672          * @deprecated use Roo.lib.Dom.getY instead
16673          * @static
16674          */
16675         getPosY: function(el) {
16676             return Roo.lib.Dom.getY(el);
16677         },
16678
16679         /**
16680          * Swap two nodes.  In IE, we use the native method, for others we
16681          * emulate the IE behavior
16682          * @method swapNode
16683          * @param n1 the first node to swap
16684          * @param n2 the other node to swap
16685          * @static
16686          */
16687         swapNode: function(n1, n2) {
16688             if (n1.swapNode) {
16689                 n1.swapNode(n2);
16690             } else {
16691                 var p = n2.parentNode;
16692                 var s = n2.nextSibling;
16693
16694                 if (s == n1) {
16695                     p.insertBefore(n1, n2);
16696                 } else if (n2 == n1.nextSibling) {
16697                     p.insertBefore(n2, n1);
16698                 } else {
16699                     n1.parentNode.replaceChild(n2, n1);
16700                     p.insertBefore(n1, s);
16701                 }
16702             }
16703         },
16704
16705         /**
16706          * Returns the current scroll position
16707          * @method getScroll
16708          * @private
16709          * @static
16710          */
16711         getScroll: function () {
16712             var t, l, dde=document.documentElement, db=document.body;
16713             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16714                 t = dde.scrollTop;
16715                 l = dde.scrollLeft;
16716             } else if (db) {
16717                 t = db.scrollTop;
16718                 l = db.scrollLeft;
16719             } else {
16720
16721             }
16722             return { top: t, left: l };
16723         },
16724
16725         /**
16726          * Returns the specified element style property
16727          * @method getStyle
16728          * @param {HTMLElement} el          the element
16729          * @param {string}      styleProp   the style property
16730          * @return {string} The value of the style property
16731          * @deprecated use Roo.lib.Dom.getStyle
16732          * @static
16733          */
16734         getStyle: function(el, styleProp) {
16735             return Roo.fly(el).getStyle(styleProp);
16736         },
16737
16738         /**
16739          * Gets the scrollTop
16740          * @method getScrollTop
16741          * @return {int} the document's scrollTop
16742          * @static
16743          */
16744         getScrollTop: function () { return this.getScroll().top; },
16745
16746         /**
16747          * Gets the scrollLeft
16748          * @method getScrollLeft
16749          * @return {int} the document's scrollTop
16750          * @static
16751          */
16752         getScrollLeft: function () { return this.getScroll().left; },
16753
16754         /**
16755          * Sets the x/y position of an element to the location of the
16756          * target element.
16757          * @method moveToEl
16758          * @param {HTMLElement} moveEl      The element to move
16759          * @param {HTMLElement} targetEl    The position reference element
16760          * @static
16761          */
16762         moveToEl: function (moveEl, targetEl) {
16763             var aCoord = Roo.lib.Dom.getXY(targetEl);
16764             Roo.lib.Dom.setXY(moveEl, aCoord);
16765         },
16766
16767         /**
16768          * Numeric array sort function
16769          * @method numericSort
16770          * @static
16771          */
16772         numericSort: function(a, b) { return (a - b); },
16773
16774         /**
16775          * Internal counter
16776          * @property _timeoutCount
16777          * @private
16778          * @static
16779          */
16780         _timeoutCount: 0,
16781
16782         /**
16783          * Trying to make the load order less important.  Without this we get
16784          * an error if this file is loaded before the Event Utility.
16785          * @method _addListeners
16786          * @private
16787          * @static
16788          */
16789         _addListeners: function() {
16790             var DDM = Roo.dd.DDM;
16791             if ( Roo.lib.Event && document ) {
16792                 DDM._onLoad();
16793             } else {
16794                 if (DDM._timeoutCount > 2000) {
16795                 } else {
16796                     setTimeout(DDM._addListeners, 10);
16797                     if (document && document.body) {
16798                         DDM._timeoutCount += 1;
16799                     }
16800                 }
16801             }
16802         },
16803
16804         /**
16805          * Recursively searches the immediate parent and all child nodes for
16806          * the handle element in order to determine wheter or not it was
16807          * clicked.
16808          * @method handleWasClicked
16809          * @param node the html element to inspect
16810          * @static
16811          */
16812         handleWasClicked: function(node, id) {
16813             if (this.isHandle(id, node.id)) {
16814                 return true;
16815             } else {
16816                 // check to see if this is a text node child of the one we want
16817                 var p = node.parentNode;
16818
16819                 while (p) {
16820                     if (this.isHandle(id, p.id)) {
16821                         return true;
16822                     } else {
16823                         p = p.parentNode;
16824                     }
16825                 }
16826             }
16827
16828             return false;
16829         }
16830
16831     };
16832
16833 }();
16834
16835 // shorter alias, save a few bytes
16836 Roo.dd.DDM = Roo.dd.DragDropMgr;
16837 Roo.dd.DDM._addListeners();
16838
16839 }/*
16840  * Based on:
16841  * Ext JS Library 1.1.1
16842  * Copyright(c) 2006-2007, Ext JS, LLC.
16843  *
16844  * Originally Released Under LGPL - original licence link has changed is not relivant.
16845  *
16846  * Fork - LGPL
16847  * <script type="text/javascript">
16848  */
16849
16850 /**
16851  * @class Roo.dd.DD
16852  * A DragDrop implementation where the linked element follows the
16853  * mouse cursor during a drag.
16854  * @extends Roo.dd.DragDrop
16855  * @constructor
16856  * @param {String} id the id of the linked element
16857  * @param {String} sGroup the group of related DragDrop items
16858  * @param {object} config an object containing configurable attributes
16859  *                Valid properties for DD:
16860  *                    scroll
16861  */
16862 Roo.dd.DD = function(id, sGroup, config) {
16863     if (id) {
16864         this.init(id, sGroup, config);
16865     }
16866 };
16867
16868 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16869
16870     /**
16871      * When set to true, the utility automatically tries to scroll the browser
16872      * window wehn a drag and drop element is dragged near the viewport boundary.
16873      * Defaults to true.
16874      * @property scroll
16875      * @type boolean
16876      */
16877     scroll: true,
16878
16879     /**
16880      * Sets the pointer offset to the distance between the linked element's top
16881      * left corner and the location the element was clicked
16882      * @method autoOffset
16883      * @param {int} iPageX the X coordinate of the click
16884      * @param {int} iPageY the Y coordinate of the click
16885      */
16886     autoOffset: function(iPageX, iPageY) {
16887         var x = iPageX - this.startPageX;
16888         var y = iPageY - this.startPageY;
16889         this.setDelta(x, y);
16890     },
16891
16892     /**
16893      * Sets the pointer offset.  You can call this directly to force the
16894      * offset to be in a particular location (e.g., pass in 0,0 to set it
16895      * to the center of the object)
16896      * @method setDelta
16897      * @param {int} iDeltaX the distance from the left
16898      * @param {int} iDeltaY the distance from the top
16899      */
16900     setDelta: function(iDeltaX, iDeltaY) {
16901         this.deltaX = iDeltaX;
16902         this.deltaY = iDeltaY;
16903     },
16904
16905     /**
16906      * Sets the drag element to the location of the mousedown or click event,
16907      * maintaining the cursor location relative to the location on the element
16908      * that was clicked.  Override this if you want to place the element in a
16909      * location other than where the cursor is.
16910      * @method setDragElPos
16911      * @param {int} iPageX the X coordinate of the mousedown or drag event
16912      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16913      */
16914     setDragElPos: function(iPageX, iPageY) {
16915         // the first time we do this, we are going to check to make sure
16916         // the element has css positioning
16917
16918         var el = this.getDragEl();
16919         this.alignElWithMouse(el, iPageX, iPageY);
16920     },
16921
16922     /**
16923      * Sets the element to the location of the mousedown or click event,
16924      * maintaining the cursor location relative to the location on the element
16925      * that was clicked.  Override this if you want to place the element in a
16926      * location other than where the cursor is.
16927      * @method alignElWithMouse
16928      * @param {HTMLElement} el the element to move
16929      * @param {int} iPageX the X coordinate of the mousedown or drag event
16930      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16931      */
16932     alignElWithMouse: function(el, iPageX, iPageY) {
16933         var oCoord = this.getTargetCoord(iPageX, iPageY);
16934         var fly = el.dom ? el : Roo.fly(el);
16935         if (!this.deltaSetXY) {
16936             var aCoord = [oCoord.x, oCoord.y];
16937             fly.setXY(aCoord);
16938             var newLeft = fly.getLeft(true);
16939             var newTop  = fly.getTop(true);
16940             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16941         } else {
16942             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16943         }
16944
16945         this.cachePosition(oCoord.x, oCoord.y);
16946         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16947         return oCoord;
16948     },
16949
16950     /**
16951      * Saves the most recent position so that we can reset the constraints and
16952      * tick marks on-demand.  We need to know this so that we can calculate the
16953      * number of pixels the element is offset from its original position.
16954      * @method cachePosition
16955      * @param iPageX the current x position (optional, this just makes it so we
16956      * don't have to look it up again)
16957      * @param iPageY the current y position (optional, this just makes it so we
16958      * don't have to look it up again)
16959      */
16960     cachePosition: function(iPageX, iPageY) {
16961         if (iPageX) {
16962             this.lastPageX = iPageX;
16963             this.lastPageY = iPageY;
16964         } else {
16965             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16966             this.lastPageX = aCoord[0];
16967             this.lastPageY = aCoord[1];
16968         }
16969     },
16970
16971     /**
16972      * Auto-scroll the window if the dragged object has been moved beyond the
16973      * visible window boundary.
16974      * @method autoScroll
16975      * @param {int} x the drag element's x position
16976      * @param {int} y the drag element's y position
16977      * @param {int} h the height of the drag element
16978      * @param {int} w the width of the drag element
16979      * @private
16980      */
16981     autoScroll: function(x, y, h, w) {
16982
16983         if (this.scroll) {
16984             // The client height
16985             var clientH = Roo.lib.Dom.getViewWidth();
16986
16987             // The client width
16988             var clientW = Roo.lib.Dom.getViewHeight();
16989
16990             // The amt scrolled down
16991             var st = this.DDM.getScrollTop();
16992
16993             // The amt scrolled right
16994             var sl = this.DDM.getScrollLeft();
16995
16996             // Location of the bottom of the element
16997             var bot = h + y;
16998
16999             // Location of the right of the element
17000             var right = w + x;
17001
17002             // The distance from the cursor to the bottom of the visible area,
17003             // adjusted so that we don't scroll if the cursor is beyond the
17004             // element drag constraints
17005             var toBot = (clientH + st - y - this.deltaY);
17006
17007             // The distance from the cursor to the right of the visible area
17008             var toRight = (clientW + sl - x - this.deltaX);
17009
17010
17011             // How close to the edge the cursor must be before we scroll
17012             // var thresh = (document.all) ? 100 : 40;
17013             var thresh = 40;
17014
17015             // How many pixels to scroll per autoscroll op.  This helps to reduce
17016             // clunky scrolling. IE is more sensitive about this ... it needs this
17017             // value to be higher.
17018             var scrAmt = (document.all) ? 80 : 30;
17019
17020             // Scroll down if we are near the bottom of the visible page and the
17021             // obj extends below the crease
17022             if ( bot > clientH && toBot < thresh ) {
17023                 window.scrollTo(sl, st + scrAmt);
17024             }
17025
17026             // Scroll up if the window is scrolled down and the top of the object
17027             // goes above the top border
17028             if ( y < st && st > 0 && y - st < thresh ) {
17029                 window.scrollTo(sl, st - scrAmt);
17030             }
17031
17032             // Scroll right if the obj is beyond the right border and the cursor is
17033             // near the border.
17034             if ( right > clientW && toRight < thresh ) {
17035                 window.scrollTo(sl + scrAmt, st);
17036             }
17037
17038             // Scroll left if the window has been scrolled to the right and the obj
17039             // extends past the left border
17040             if ( x < sl && sl > 0 && x - sl < thresh ) {
17041                 window.scrollTo(sl - scrAmt, st);
17042             }
17043         }
17044     },
17045
17046     /**
17047      * Finds the location the element should be placed if we want to move
17048      * it to where the mouse location less the click offset would place us.
17049      * @method getTargetCoord
17050      * @param {int} iPageX the X coordinate of the click
17051      * @param {int} iPageY the Y coordinate of the click
17052      * @return an object that contains the coordinates (Object.x and Object.y)
17053      * @private
17054      */
17055     getTargetCoord: function(iPageX, iPageY) {
17056
17057
17058         var x = iPageX - this.deltaX;
17059         var y = iPageY - this.deltaY;
17060
17061         if (this.constrainX) {
17062             if (x < this.minX) { x = this.minX; }
17063             if (x > this.maxX) { x = this.maxX; }
17064         }
17065
17066         if (this.constrainY) {
17067             if (y < this.minY) { y = this.minY; }
17068             if (y > this.maxY) { y = this.maxY; }
17069         }
17070
17071         x = this.getTick(x, this.xTicks);
17072         y = this.getTick(y, this.yTicks);
17073
17074
17075         return {x:x, y:y};
17076     },
17077
17078     /*
17079      * Sets up config options specific to this class. Overrides
17080      * Roo.dd.DragDrop, but all versions of this method through the
17081      * inheritance chain are called
17082      */
17083     applyConfig: function() {
17084         Roo.dd.DD.superclass.applyConfig.call(this);
17085         this.scroll = (this.config.scroll !== false);
17086     },
17087
17088     /*
17089      * Event that fires prior to the onMouseDown event.  Overrides
17090      * Roo.dd.DragDrop.
17091      */
17092     b4MouseDown: function(e) {
17093         // this.resetConstraints();
17094         this.autoOffset(e.getPageX(),
17095                             e.getPageY());
17096     },
17097
17098     /*
17099      * Event that fires prior to the onDrag event.  Overrides
17100      * Roo.dd.DragDrop.
17101      */
17102     b4Drag: function(e) {
17103         this.setDragElPos(e.getPageX(),
17104                             e.getPageY());
17105     },
17106
17107     toString: function() {
17108         return ("DD " + this.id);
17109     }
17110
17111     //////////////////////////////////////////////////////////////////////////
17112     // Debugging ygDragDrop events that can be overridden
17113     //////////////////////////////////////////////////////////////////////////
17114     /*
17115     startDrag: function(x, y) {
17116     },
17117
17118     onDrag: function(e) {
17119     },
17120
17121     onDragEnter: function(e, id) {
17122     },
17123
17124     onDragOver: function(e, id) {
17125     },
17126
17127     onDragOut: function(e, id) {
17128     },
17129
17130     onDragDrop: function(e, id) {
17131     },
17132
17133     endDrag: function(e) {
17134     }
17135
17136     */
17137
17138 });/*
17139  * Based on:
17140  * Ext JS Library 1.1.1
17141  * Copyright(c) 2006-2007, Ext JS, LLC.
17142  *
17143  * Originally Released Under LGPL - original licence link has changed is not relivant.
17144  *
17145  * Fork - LGPL
17146  * <script type="text/javascript">
17147  */
17148
17149 /**
17150  * @class Roo.dd.DDProxy
17151  * A DragDrop implementation that inserts an empty, bordered div into
17152  * the document that follows the cursor during drag operations.  At the time of
17153  * the click, the frame div is resized to the dimensions of the linked html
17154  * element, and moved to the exact location of the linked element.
17155  *
17156  * References to the "frame" element refer to the single proxy element that
17157  * was created to be dragged in place of all DDProxy elements on the
17158  * page.
17159  *
17160  * @extends Roo.dd.DD
17161  * @constructor
17162  * @param {String} id the id of the linked html element
17163  * @param {String} sGroup the group of related DragDrop objects
17164  * @param {object} config an object containing configurable attributes
17165  *                Valid properties for DDProxy in addition to those in DragDrop:
17166  *                   resizeFrame, centerFrame, dragElId
17167  */
17168 Roo.dd.DDProxy = function(id, sGroup, config) {
17169     if (id) {
17170         this.init(id, sGroup, config);
17171         this.initFrame();
17172     }
17173 };
17174
17175 /**
17176  * The default drag frame div id
17177  * @property Roo.dd.DDProxy.dragElId
17178  * @type String
17179  * @static
17180  */
17181 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17182
17183 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17184
17185     /**
17186      * By default we resize the drag frame to be the same size as the element
17187      * we want to drag (this is to get the frame effect).  We can turn it off
17188      * if we want a different behavior.
17189      * @property resizeFrame
17190      * @type boolean
17191      */
17192     resizeFrame: true,
17193
17194     /**
17195      * By default the frame is positioned exactly where the drag element is, so
17196      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17197      * you do not have constraints on the obj is to have the drag frame centered
17198      * around the cursor.  Set centerFrame to true for this effect.
17199      * @property centerFrame
17200      * @type boolean
17201      */
17202     centerFrame: false,
17203
17204     /**
17205      * Creates the proxy element if it does not yet exist
17206      * @method createFrame
17207      */
17208     createFrame: function() {
17209         var self = this;
17210         var body = document.body;
17211
17212         if (!body || !body.firstChild) {
17213             setTimeout( function() { self.createFrame(); }, 50 );
17214             return;
17215         }
17216
17217         var div = this.getDragEl();
17218
17219         if (!div) {
17220             div    = document.createElement("div");
17221             div.id = this.dragElId;
17222             var s  = div.style;
17223
17224             s.position   = "absolute";
17225             s.visibility = "hidden";
17226             s.cursor     = "move";
17227             s.border     = "2px solid #aaa";
17228             s.zIndex     = 999;
17229
17230             // appendChild can blow up IE if invoked prior to the window load event
17231             // while rendering a table.  It is possible there are other scenarios
17232             // that would cause this to happen as well.
17233             body.insertBefore(div, body.firstChild);
17234         }
17235     },
17236
17237     /**
17238      * Initialization for the drag frame element.  Must be called in the
17239      * constructor of all subclasses
17240      * @method initFrame
17241      */
17242     initFrame: function() {
17243         this.createFrame();
17244     },
17245
17246     applyConfig: function() {
17247         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17248
17249         this.resizeFrame = (this.config.resizeFrame !== false);
17250         this.centerFrame = (this.config.centerFrame);
17251         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17252     },
17253
17254     /**
17255      * Resizes the drag frame to the dimensions of the clicked object, positions
17256      * it over the object, and finally displays it
17257      * @method showFrame
17258      * @param {int} iPageX X click position
17259      * @param {int} iPageY Y click position
17260      * @private
17261      */
17262     showFrame: function(iPageX, iPageY) {
17263         var el = this.getEl();
17264         var dragEl = this.getDragEl();
17265         var s = dragEl.style;
17266
17267         this._resizeProxy();
17268
17269         if (this.centerFrame) {
17270             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17271                            Math.round(parseInt(s.height, 10)/2) );
17272         }
17273
17274         this.setDragElPos(iPageX, iPageY);
17275
17276         Roo.fly(dragEl).show();
17277     },
17278
17279     /**
17280      * The proxy is automatically resized to the dimensions of the linked
17281      * element when a drag is initiated, unless resizeFrame is set to false
17282      * @method _resizeProxy
17283      * @private
17284      */
17285     _resizeProxy: function() {
17286         if (this.resizeFrame) {
17287             var el = this.getEl();
17288             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17289         }
17290     },
17291
17292     // overrides Roo.dd.DragDrop
17293     b4MouseDown: function(e) {
17294         var x = e.getPageX();
17295         var y = e.getPageY();
17296         this.autoOffset(x, y);
17297         this.setDragElPos(x, y);
17298     },
17299
17300     // overrides Roo.dd.DragDrop
17301     b4StartDrag: function(x, y) {
17302         // show the drag frame
17303         this.showFrame(x, y);
17304     },
17305
17306     // overrides Roo.dd.DragDrop
17307     b4EndDrag: function(e) {
17308         Roo.fly(this.getDragEl()).hide();
17309     },
17310
17311     // overrides Roo.dd.DragDrop
17312     // By default we try to move the element to the last location of the frame.
17313     // This is so that the default behavior mirrors that of Roo.dd.DD.
17314     endDrag: function(e) {
17315
17316         var lel = this.getEl();
17317         var del = this.getDragEl();
17318
17319         // Show the drag frame briefly so we can get its position
17320         del.style.visibility = "";
17321
17322         this.beforeMove();
17323         // Hide the linked element before the move to get around a Safari
17324         // rendering bug.
17325         lel.style.visibility = "hidden";
17326         Roo.dd.DDM.moveToEl(lel, del);
17327         del.style.visibility = "hidden";
17328         lel.style.visibility = "";
17329
17330         this.afterDrag();
17331     },
17332
17333     beforeMove : function(){
17334
17335     },
17336
17337     afterDrag : function(){
17338
17339     },
17340
17341     toString: function() {
17342         return ("DDProxy " + this.id);
17343     }
17344
17345 });
17346 /*
17347  * Based on:
17348  * Ext JS Library 1.1.1
17349  * Copyright(c) 2006-2007, Ext JS, LLC.
17350  *
17351  * Originally Released Under LGPL - original licence link has changed is not relivant.
17352  *
17353  * Fork - LGPL
17354  * <script type="text/javascript">
17355  */
17356
17357  /**
17358  * @class Roo.dd.DDTarget
17359  * A DragDrop implementation that does not move, but can be a drop
17360  * target.  You would get the same result by simply omitting implementation
17361  * for the event callbacks, but this way we reduce the processing cost of the
17362  * event listener and the callbacks.
17363  * @extends Roo.dd.DragDrop
17364  * @constructor
17365  * @param {String} id the id of the element that is a drop target
17366  * @param {String} sGroup the group of related DragDrop objects
17367  * @param {object} config an object containing configurable attributes
17368  *                 Valid properties for DDTarget in addition to those in
17369  *                 DragDrop:
17370  *                    none
17371  */
17372 Roo.dd.DDTarget = function(id, sGroup, config) {
17373     if (id) {
17374         this.initTarget(id, sGroup, config);
17375     }
17376 };
17377
17378 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17379 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17380     toString: function() {
17381         return ("DDTarget " + this.id);
17382     }
17383 });
17384 /*
17385  * Based on:
17386  * Ext JS Library 1.1.1
17387  * Copyright(c) 2006-2007, Ext JS, LLC.
17388  *
17389  * Originally Released Under LGPL - original licence link has changed is not relivant.
17390  *
17391  * Fork - LGPL
17392  * <script type="text/javascript">
17393  */
17394  
17395
17396 /**
17397  * @class Roo.dd.ScrollManager
17398  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17399  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17400  * @singleton
17401  */
17402 Roo.dd.ScrollManager = function(){
17403     var ddm = Roo.dd.DragDropMgr;
17404     var els = {};
17405     var dragEl = null;
17406     var proc = {};
17407     
17408     var onStop = function(e){
17409         dragEl = null;
17410         clearProc();
17411     };
17412     
17413     var triggerRefresh = function(){
17414         if(ddm.dragCurrent){
17415              ddm.refreshCache(ddm.dragCurrent.groups);
17416         }
17417     };
17418     
17419     var doScroll = function(){
17420         if(ddm.dragCurrent){
17421             var dds = Roo.dd.ScrollManager;
17422             if(!dds.animate){
17423                 if(proc.el.scroll(proc.dir, dds.increment)){
17424                     triggerRefresh();
17425                 }
17426             }else{
17427                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17428             }
17429         }
17430     };
17431     
17432     var clearProc = function(){
17433         if(proc.id){
17434             clearInterval(proc.id);
17435         }
17436         proc.id = 0;
17437         proc.el = null;
17438         proc.dir = "";
17439     };
17440     
17441     var startProc = function(el, dir){
17442         clearProc();
17443         proc.el = el;
17444         proc.dir = dir;
17445         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17446     };
17447     
17448     var onFire = function(e, isDrop){
17449         if(isDrop || !ddm.dragCurrent){ return; }
17450         var dds = Roo.dd.ScrollManager;
17451         if(!dragEl || dragEl != ddm.dragCurrent){
17452             dragEl = ddm.dragCurrent;
17453             // refresh regions on drag start
17454             dds.refreshCache();
17455         }
17456         
17457         var xy = Roo.lib.Event.getXY(e);
17458         var pt = new Roo.lib.Point(xy[0], xy[1]);
17459         for(var id in els){
17460             var el = els[id], r = el._region;
17461             if(r && r.contains(pt) && el.isScrollable()){
17462                 if(r.bottom - pt.y <= dds.thresh){
17463                     if(proc.el != el){
17464                         startProc(el, "down");
17465                     }
17466                     return;
17467                 }else if(r.right - pt.x <= dds.thresh){
17468                     if(proc.el != el){
17469                         startProc(el, "left");
17470                     }
17471                     return;
17472                 }else if(pt.y - r.top <= dds.thresh){
17473                     if(proc.el != el){
17474                         startProc(el, "up");
17475                     }
17476                     return;
17477                 }else if(pt.x - r.left <= dds.thresh){
17478                     if(proc.el != el){
17479                         startProc(el, "right");
17480                     }
17481                     return;
17482                 }
17483             }
17484         }
17485         clearProc();
17486     };
17487     
17488     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17489     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17490     
17491     return {
17492         /**
17493          * Registers new overflow element(s) to auto scroll
17494          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17495          */
17496         register : function(el){
17497             if(el instanceof Array){
17498                 for(var i = 0, len = el.length; i < len; i++) {
17499                         this.register(el[i]);
17500                 }
17501             }else{
17502                 el = Roo.get(el);
17503                 els[el.id] = el;
17504             }
17505         },
17506         
17507         /**
17508          * Unregisters overflow element(s) so they are no longer scrolled
17509          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17510          */
17511         unregister : function(el){
17512             if(el instanceof Array){
17513                 for(var i = 0, len = el.length; i < len; i++) {
17514                         this.unregister(el[i]);
17515                 }
17516             }else{
17517                 el = Roo.get(el);
17518                 delete els[el.id];
17519             }
17520         },
17521         
17522         /**
17523          * The number of pixels from the edge of a container the pointer needs to be to 
17524          * trigger scrolling (defaults to 25)
17525          * @type Number
17526          */
17527         thresh : 25,
17528         
17529         /**
17530          * The number of pixels to scroll in each scroll increment (defaults to 50)
17531          * @type Number
17532          */
17533         increment : 100,
17534         
17535         /**
17536          * The frequency of scrolls in milliseconds (defaults to 500)
17537          * @type Number
17538          */
17539         frequency : 500,
17540         
17541         /**
17542          * True to animate the scroll (defaults to true)
17543          * @type Boolean
17544          */
17545         animate: true,
17546         
17547         /**
17548          * The animation duration in seconds - 
17549          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17550          * @type Number
17551          */
17552         animDuration: .4,
17553         
17554         /**
17555          * Manually trigger a cache refresh.
17556          */
17557         refreshCache : function(){
17558             for(var id in els){
17559                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17560                     els[id]._region = els[id].getRegion();
17561                 }
17562             }
17563         }
17564     };
17565 }();/*
17566  * Based on:
17567  * Ext JS Library 1.1.1
17568  * Copyright(c) 2006-2007, Ext JS, LLC.
17569  *
17570  * Originally Released Under LGPL - original licence link has changed is not relivant.
17571  *
17572  * Fork - LGPL
17573  * <script type="text/javascript">
17574  */
17575  
17576
17577 /**
17578  * @class Roo.dd.Registry
17579  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17580  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17581  * @singleton
17582  */
17583 Roo.dd.Registry = function(){
17584     var elements = {}; 
17585     var handles = {}; 
17586     var autoIdSeed = 0;
17587
17588     var getId = function(el, autogen){
17589         if(typeof el == "string"){
17590             return el;
17591         }
17592         var id = el.id;
17593         if(!id && autogen !== false){
17594             id = "roodd-" + (++autoIdSeed);
17595             el.id = id;
17596         }
17597         return id;
17598     };
17599     
17600     return {
17601     /**
17602      * Register a drag drop element
17603      * @param {String|HTMLElement} element The id or DOM node to register
17604      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17605      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17606      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17607      * populated in the data object (if applicable):
17608      * <pre>
17609 Value      Description<br />
17610 ---------  ------------------------------------------<br />
17611 handles    Array of DOM nodes that trigger dragging<br />
17612            for the element being registered<br />
17613 isHandle   True if the element passed in triggers<br />
17614            dragging itself, else false
17615 </pre>
17616      */
17617         register : function(el, data){
17618             data = data || {};
17619             if(typeof el == "string"){
17620                 el = document.getElementById(el);
17621             }
17622             data.ddel = el;
17623             elements[getId(el)] = data;
17624             if(data.isHandle !== false){
17625                 handles[data.ddel.id] = data;
17626             }
17627             if(data.handles){
17628                 var hs = data.handles;
17629                 for(var i = 0, len = hs.length; i < len; i++){
17630                         handles[getId(hs[i])] = data;
17631                 }
17632             }
17633         },
17634
17635     /**
17636      * Unregister a drag drop element
17637      * @param {String|HTMLElement}  element The id or DOM node to unregister
17638      */
17639         unregister : function(el){
17640             var id = getId(el, false);
17641             var data = elements[id];
17642             if(data){
17643                 delete elements[id];
17644                 if(data.handles){
17645                     var hs = data.handles;
17646                     for(var i = 0, len = hs.length; i < len; i++){
17647                         delete handles[getId(hs[i], false)];
17648                     }
17649                 }
17650             }
17651         },
17652
17653     /**
17654      * Returns the handle registered for a DOM Node by id
17655      * @param {String|HTMLElement} id The DOM node or id to look up
17656      * @return {Object} handle The custom handle data
17657      */
17658         getHandle : function(id){
17659             if(typeof id != "string"){ // must be element?
17660                 id = id.id;
17661             }
17662             return handles[id];
17663         },
17664
17665     /**
17666      * Returns the handle that is registered for the DOM node that is the target of the event
17667      * @param {Event} e The event
17668      * @return {Object} handle The custom handle data
17669      */
17670         getHandleFromEvent : function(e){
17671             var t = Roo.lib.Event.getTarget(e);
17672             return t ? handles[t.id] : null;
17673         },
17674
17675     /**
17676      * Returns a custom data object that is registered for a DOM node by id
17677      * @param {String|HTMLElement} id The DOM node or id to look up
17678      * @return {Object} data The custom data
17679      */
17680         getTarget : function(id){
17681             if(typeof id != "string"){ // must be element?
17682                 id = id.id;
17683             }
17684             return elements[id];
17685         },
17686
17687     /**
17688      * Returns a custom data object that is registered for the DOM node that is the target of the event
17689      * @param {Event} e The event
17690      * @return {Object} data The custom data
17691      */
17692         getTargetFromEvent : function(e){
17693             var t = Roo.lib.Event.getTarget(e);
17694             return t ? elements[t.id] || handles[t.id] : null;
17695         }
17696     };
17697 }();/*
17698  * Based on:
17699  * Ext JS Library 1.1.1
17700  * Copyright(c) 2006-2007, Ext JS, LLC.
17701  *
17702  * Originally Released Under LGPL - original licence link has changed is not relivant.
17703  *
17704  * Fork - LGPL
17705  * <script type="text/javascript">
17706  */
17707  
17708
17709 /**
17710  * @class Roo.dd.StatusProxy
17711  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17712  * default drag proxy used by all Roo.dd components.
17713  * @constructor
17714  * @param {Object} config
17715  */
17716 Roo.dd.StatusProxy = function(config){
17717     Roo.apply(this, config);
17718     this.id = this.id || Roo.id();
17719     this.el = new Roo.Layer({
17720         dh: {
17721             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17722                 {tag: "div", cls: "x-dd-drop-icon"},
17723                 {tag: "div", cls: "x-dd-drag-ghost"}
17724             ]
17725         }, 
17726         shadow: !config || config.shadow !== false
17727     });
17728     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17729     this.dropStatus = this.dropNotAllowed;
17730 };
17731
17732 Roo.dd.StatusProxy.prototype = {
17733     /**
17734      * @cfg {String} dropAllowed
17735      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17736      */
17737     dropAllowed : "x-dd-drop-ok",
17738     /**
17739      * @cfg {String} dropNotAllowed
17740      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17741      */
17742     dropNotAllowed : "x-dd-drop-nodrop",
17743
17744     /**
17745      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17746      * over the current target element.
17747      * @param {String} cssClass The css class for the new drop status indicator image
17748      */
17749     setStatus : function(cssClass){
17750         cssClass = cssClass || this.dropNotAllowed;
17751         if(this.dropStatus != cssClass){
17752             this.el.replaceClass(this.dropStatus, cssClass);
17753             this.dropStatus = cssClass;
17754         }
17755     },
17756
17757     /**
17758      * Resets the status indicator to the default dropNotAllowed value
17759      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17760      */
17761     reset : function(clearGhost){
17762         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17763         this.dropStatus = this.dropNotAllowed;
17764         if(clearGhost){
17765             this.ghost.update("");
17766         }
17767     },
17768
17769     /**
17770      * Updates the contents of the ghost element
17771      * @param {String} html The html that will replace the current innerHTML of the ghost element
17772      */
17773     update : function(html){
17774         if(typeof html == "string"){
17775             this.ghost.update(html);
17776         }else{
17777             this.ghost.update("");
17778             html.style.margin = "0";
17779             this.ghost.dom.appendChild(html);
17780         }
17781         // ensure float = none set?? cant remember why though.
17782         var el = this.ghost.dom.firstChild;
17783                 if(el){
17784                         Roo.fly(el).setStyle('float', 'none');
17785                 }
17786     },
17787     
17788     /**
17789      * Returns the underlying proxy {@link Roo.Layer}
17790      * @return {Roo.Layer} el
17791     */
17792     getEl : function(){
17793         return this.el;
17794     },
17795
17796     /**
17797      * Returns the ghost element
17798      * @return {Roo.Element} el
17799      */
17800     getGhost : function(){
17801         return this.ghost;
17802     },
17803
17804     /**
17805      * Hides the proxy
17806      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17807      */
17808     hide : function(clear){
17809         this.el.hide();
17810         if(clear){
17811             this.reset(true);
17812         }
17813     },
17814
17815     /**
17816      * Stops the repair animation if it's currently running
17817      */
17818     stop : function(){
17819         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17820             this.anim.stop();
17821         }
17822     },
17823
17824     /**
17825      * Displays this proxy
17826      */
17827     show : function(){
17828         this.el.show();
17829     },
17830
17831     /**
17832      * Force the Layer to sync its shadow and shim positions to the element
17833      */
17834     sync : function(){
17835         this.el.sync();
17836     },
17837
17838     /**
17839      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17840      * invalid drop operation by the item being dragged.
17841      * @param {Array} xy The XY position of the element ([x, y])
17842      * @param {Function} callback The function to call after the repair is complete
17843      * @param {Object} scope The scope in which to execute the callback
17844      */
17845     repair : function(xy, callback, scope){
17846         this.callback = callback;
17847         this.scope = scope;
17848         if(xy && this.animRepair !== false){
17849             this.el.addClass("x-dd-drag-repair");
17850             this.el.hideUnders(true);
17851             this.anim = this.el.shift({
17852                 duration: this.repairDuration || .5,
17853                 easing: 'easeOut',
17854                 xy: xy,
17855                 stopFx: true,
17856                 callback: this.afterRepair,
17857                 scope: this
17858             });
17859         }else{
17860             this.afterRepair();
17861         }
17862     },
17863
17864     // private
17865     afterRepair : function(){
17866         this.hide(true);
17867         if(typeof this.callback == "function"){
17868             this.callback.call(this.scope || this);
17869         }
17870         this.callback = null;
17871         this.scope = null;
17872     }
17873 };/*
17874  * Based on:
17875  * Ext JS Library 1.1.1
17876  * Copyright(c) 2006-2007, Ext JS, LLC.
17877  *
17878  * Originally Released Under LGPL - original licence link has changed is not relivant.
17879  *
17880  * Fork - LGPL
17881  * <script type="text/javascript">
17882  */
17883
17884 /**
17885  * @class Roo.dd.DragSource
17886  * @extends Roo.dd.DDProxy
17887  * A simple class that provides the basic implementation needed to make any element draggable.
17888  * @constructor
17889  * @param {String/HTMLElement/Element} el The container element
17890  * @param {Object} config
17891  */
17892 Roo.dd.DragSource = function(el, config){
17893     this.el = Roo.get(el);
17894     this.dragData = {};
17895     
17896     Roo.apply(this, config);
17897     
17898     if(!this.proxy){
17899         this.proxy = new Roo.dd.StatusProxy();
17900     }
17901
17902     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17903           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17904     
17905     this.dragging = false;
17906 };
17907
17908 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17909     /**
17910      * @cfg {String} dropAllowed
17911      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17912      */
17913     dropAllowed : "x-dd-drop-ok",
17914     /**
17915      * @cfg {String} dropNotAllowed
17916      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17917      */
17918     dropNotAllowed : "x-dd-drop-nodrop",
17919
17920     /**
17921      * Returns the data object associated with this drag source
17922      * @return {Object} data An object containing arbitrary data
17923      */
17924     getDragData : function(e){
17925         return this.dragData;
17926     },
17927
17928     // private
17929     onDragEnter : function(e, id){
17930         var target = Roo.dd.DragDropMgr.getDDById(id);
17931         this.cachedTarget = target;
17932         if(this.beforeDragEnter(target, e, id) !== false){
17933             if(target.isNotifyTarget){
17934                 var status = target.notifyEnter(this, e, this.dragData);
17935                 this.proxy.setStatus(status);
17936             }else{
17937                 this.proxy.setStatus(this.dropAllowed);
17938             }
17939             
17940             if(this.afterDragEnter){
17941                 /**
17942                  * An empty function by default, but provided so that you can perform a custom action
17943                  * when the dragged item enters the drop target by providing an implementation.
17944                  * @param {Roo.dd.DragDrop} target The drop target
17945                  * @param {Event} e The event object
17946                  * @param {String} id The id of the dragged element
17947                  * @method afterDragEnter
17948                  */
17949                 this.afterDragEnter(target, e, id);
17950             }
17951         }
17952     },
17953
17954     /**
17955      * An empty function by default, but provided so that you can perform a custom action
17956      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17957      * @param {Roo.dd.DragDrop} target The drop target
17958      * @param {Event} e The event object
17959      * @param {String} id The id of the dragged element
17960      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17961      */
17962     beforeDragEnter : function(target, e, id){
17963         return true;
17964     },
17965
17966     // private
17967     alignElWithMouse: function() {
17968         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17969         this.proxy.sync();
17970     },
17971
17972     // private
17973     onDragOver : function(e, id){
17974         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17975         if(this.beforeDragOver(target, e, id) !== false){
17976             if(target.isNotifyTarget){
17977                 var status = target.notifyOver(this, e, this.dragData);
17978                 this.proxy.setStatus(status);
17979             }
17980
17981             if(this.afterDragOver){
17982                 /**
17983                  * An empty function by default, but provided so that you can perform a custom action
17984                  * while the dragged item is over the drop target by providing an implementation.
17985                  * @param {Roo.dd.DragDrop} target The drop target
17986                  * @param {Event} e The event object
17987                  * @param {String} id The id of the dragged element
17988                  * @method afterDragOver
17989                  */
17990                 this.afterDragOver(target, e, id);
17991             }
17992         }
17993     },
17994
17995     /**
17996      * An empty function by default, but provided so that you can perform a custom action
17997      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17998      * @param {Roo.dd.DragDrop} target The drop target
17999      * @param {Event} e The event object
18000      * @param {String} id The id of the dragged element
18001      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18002      */
18003     beforeDragOver : function(target, e, id){
18004         return true;
18005     },
18006
18007     // private
18008     onDragOut : function(e, id){
18009         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18010         if(this.beforeDragOut(target, e, id) !== false){
18011             if(target.isNotifyTarget){
18012                 target.notifyOut(this, e, this.dragData);
18013             }
18014             this.proxy.reset();
18015             if(this.afterDragOut){
18016                 /**
18017                  * An empty function by default, but provided so that you can perform a custom action
18018                  * after the dragged item is dragged out of the target without dropping.
18019                  * @param {Roo.dd.DragDrop} target The drop target
18020                  * @param {Event} e The event object
18021                  * @param {String} id The id of the dragged element
18022                  * @method afterDragOut
18023                  */
18024                 this.afterDragOut(target, e, id);
18025             }
18026         }
18027         this.cachedTarget = null;
18028     },
18029
18030     /**
18031      * An empty function by default, but provided so that you can perform a custom action before the dragged
18032      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18033      * @param {Roo.dd.DragDrop} target The drop target
18034      * @param {Event} e The event object
18035      * @param {String} id The id of the dragged element
18036      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18037      */
18038     beforeDragOut : function(target, e, id){
18039         return true;
18040     },
18041     
18042     // private
18043     onDragDrop : function(e, id){
18044         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18045         if(this.beforeDragDrop(target, e, id) !== false){
18046             if(target.isNotifyTarget){
18047                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18048                     this.onValidDrop(target, e, id);
18049                 }else{
18050                     this.onInvalidDrop(target, e, id);
18051                 }
18052             }else{
18053                 this.onValidDrop(target, e, id);
18054             }
18055             
18056             if(this.afterDragDrop){
18057                 /**
18058                  * An empty function by default, but provided so that you can perform a custom action
18059                  * after a valid drag drop has occurred by providing an implementation.
18060                  * @param {Roo.dd.DragDrop} target The drop target
18061                  * @param {Event} e The event object
18062                  * @param {String} id The id of the dropped element
18063                  * @method afterDragDrop
18064                  */
18065                 this.afterDragDrop(target, e, id);
18066             }
18067         }
18068         delete this.cachedTarget;
18069     },
18070
18071     /**
18072      * An empty function by default, but provided so that you can perform a custom action before the dragged
18073      * item is dropped onto the target and optionally cancel the onDragDrop.
18074      * @param {Roo.dd.DragDrop} target The drop target
18075      * @param {Event} e The event object
18076      * @param {String} id The id of the dragged element
18077      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18078      */
18079     beforeDragDrop : function(target, e, id){
18080         return true;
18081     },
18082
18083     // private
18084     onValidDrop : function(target, e, id){
18085         this.hideProxy();
18086         if(this.afterValidDrop){
18087             /**
18088              * An empty function by default, but provided so that you can perform a custom action
18089              * after a valid drop has occurred by providing an implementation.
18090              * @param {Object} target The target DD 
18091              * @param {Event} e The event object
18092              * @param {String} id The id of the dropped element
18093              * @method afterInvalidDrop
18094              */
18095             this.afterValidDrop(target, e, id);
18096         }
18097     },
18098
18099     // private
18100     getRepairXY : function(e, data){
18101         return this.el.getXY();  
18102     },
18103
18104     // private
18105     onInvalidDrop : function(target, e, id){
18106         this.beforeInvalidDrop(target, e, id);
18107         if(this.cachedTarget){
18108             if(this.cachedTarget.isNotifyTarget){
18109                 this.cachedTarget.notifyOut(this, e, this.dragData);
18110             }
18111             this.cacheTarget = null;
18112         }
18113         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18114
18115         if(this.afterInvalidDrop){
18116             /**
18117              * An empty function by default, but provided so that you can perform a custom action
18118              * after an invalid drop has occurred by providing an implementation.
18119              * @param {Event} e The event object
18120              * @param {String} id The id of the dropped element
18121              * @method afterInvalidDrop
18122              */
18123             this.afterInvalidDrop(e, id);
18124         }
18125     },
18126
18127     // private
18128     afterRepair : function(){
18129         if(Roo.enableFx){
18130             this.el.highlight(this.hlColor || "c3daf9");
18131         }
18132         this.dragging = false;
18133     },
18134
18135     /**
18136      * An empty function by default, but provided so that you can perform a custom action after an invalid
18137      * drop has occurred.
18138      * @param {Roo.dd.DragDrop} target The drop target
18139      * @param {Event} e The event object
18140      * @param {String} id The id of the dragged element
18141      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18142      */
18143     beforeInvalidDrop : function(target, e, id){
18144         return true;
18145     },
18146
18147     // private
18148     handleMouseDown : function(e){
18149         if(this.dragging) {
18150             return;
18151         }
18152         var data = this.getDragData(e);
18153         if(data && this.onBeforeDrag(data, e) !== false){
18154             this.dragData = data;
18155             this.proxy.stop();
18156             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18157         } 
18158     },
18159
18160     /**
18161      * An empty function by default, but provided so that you can perform a custom action before the initial
18162      * drag event begins and optionally cancel it.
18163      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18164      * @param {Event} e The event object
18165      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18166      */
18167     onBeforeDrag : function(data, e){
18168         return true;
18169     },
18170
18171     /**
18172      * An empty function by default, but provided so that you can perform a custom action once the initial
18173      * drag event has begun.  The drag cannot be canceled from this function.
18174      * @param {Number} x The x position of the click on the dragged object
18175      * @param {Number} y The y position of the click on the dragged object
18176      */
18177     onStartDrag : Roo.emptyFn,
18178
18179     // private - YUI override
18180     startDrag : function(x, y){
18181         this.proxy.reset();
18182         this.dragging = true;
18183         this.proxy.update("");
18184         this.onInitDrag(x, y);
18185         this.proxy.show();
18186     },
18187
18188     // private
18189     onInitDrag : function(x, y){
18190         var clone = this.el.dom.cloneNode(true);
18191         clone.id = Roo.id(); // prevent duplicate ids
18192         this.proxy.update(clone);
18193         this.onStartDrag(x, y);
18194         return true;
18195     },
18196
18197     /**
18198      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18199      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18200      */
18201     getProxy : function(){
18202         return this.proxy;  
18203     },
18204
18205     /**
18206      * Hides the drag source's {@link Roo.dd.StatusProxy}
18207      */
18208     hideProxy : function(){
18209         this.proxy.hide();  
18210         this.proxy.reset(true);
18211         this.dragging = false;
18212     },
18213
18214     // private
18215     triggerCacheRefresh : function(){
18216         Roo.dd.DDM.refreshCache(this.groups);
18217     },
18218
18219     // private - override to prevent hiding
18220     b4EndDrag: function(e) {
18221     },
18222
18223     // private - override to prevent moving
18224     endDrag : function(e){
18225         this.onEndDrag(this.dragData, e);
18226     },
18227
18228     // private
18229     onEndDrag : function(data, e){
18230     },
18231     
18232     // private - pin to cursor
18233     autoOffset : function(x, y) {
18234         this.setDelta(-12, -20);
18235     }    
18236 });/*
18237  * Based on:
18238  * Ext JS Library 1.1.1
18239  * Copyright(c) 2006-2007, Ext JS, LLC.
18240  *
18241  * Originally Released Under LGPL - original licence link has changed is not relivant.
18242  *
18243  * Fork - LGPL
18244  * <script type="text/javascript">
18245  */
18246
18247
18248 /**
18249  * @class Roo.dd.DropTarget
18250  * @extends Roo.dd.DDTarget
18251  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18252  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18253  * @constructor
18254  * @param {String/HTMLElement/Element} el The container element
18255  * @param {Object} config
18256  */
18257 Roo.dd.DropTarget = function(el, config){
18258     this.el = Roo.get(el);
18259     
18260     Roo.apply(this, config);
18261     
18262     if(this.containerScroll){
18263         Roo.dd.ScrollManager.register(this.el);
18264     }
18265     
18266     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18267           {isTarget: true});
18268
18269 };
18270
18271 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18272     /**
18273      * @cfg {String} overClass
18274      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18275      */
18276     /**
18277      * @cfg {String} dropAllowed
18278      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18279      */
18280     dropAllowed : "x-dd-drop-ok",
18281     /**
18282      * @cfg {String} dropNotAllowed
18283      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18284      */
18285     dropNotAllowed : "x-dd-drop-nodrop",
18286
18287     // private
18288     isTarget : true,
18289
18290     // private
18291     isNotifyTarget : true,
18292
18293     /**
18294      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18295      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18296      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18297      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18298      * @param {Event} e The event
18299      * @param {Object} data An object containing arbitrary data supplied by the drag source
18300      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18301      * underlying {@link Roo.dd.StatusProxy} can be updated
18302      */
18303     notifyEnter : function(dd, e, data){
18304         if(this.overClass){
18305             this.el.addClass(this.overClass);
18306         }
18307         return this.dropAllowed;
18308     },
18309
18310     /**
18311      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18312      * This method will be called on every mouse movement while the drag source is over the drop target.
18313      * This default implementation simply returns the dropAllowed config value.
18314      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18315      * @param {Event} e The event
18316      * @param {Object} data An object containing arbitrary data supplied by the drag source
18317      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18318      * underlying {@link Roo.dd.StatusProxy} can be updated
18319      */
18320     notifyOver : function(dd, e, data){
18321         return this.dropAllowed;
18322     },
18323
18324     /**
18325      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18326      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18327      * overClass (if any) from the drop element.
18328      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18329      * @param {Event} e The event
18330      * @param {Object} data An object containing arbitrary data supplied by the drag source
18331      */
18332     notifyOut : function(dd, e, data){
18333         if(this.overClass){
18334             this.el.removeClass(this.overClass);
18335         }
18336     },
18337
18338     /**
18339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18340      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18341      * implementation that does something to process the drop event and returns true so that the drag source's
18342      * repair action does not run.
18343      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18344      * @param {Event} e The event
18345      * @param {Object} data An object containing arbitrary data supplied by the drag source
18346      * @return {Boolean} True if the drop was valid, else false
18347      */
18348     notifyDrop : function(dd, e, data){
18349         return false;
18350     }
18351 });/*
18352  * Based on:
18353  * Ext JS Library 1.1.1
18354  * Copyright(c) 2006-2007, Ext JS, LLC.
18355  *
18356  * Originally Released Under LGPL - original licence link has changed is not relivant.
18357  *
18358  * Fork - LGPL
18359  * <script type="text/javascript">
18360  */
18361
18362
18363 /**
18364  * @class Roo.dd.DragZone
18365  * @extends Roo.dd.DragSource
18366  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18367  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18368  * @constructor
18369  * @param {String/HTMLElement/Element} el The container element
18370  * @param {Object} config
18371  */
18372 Roo.dd.DragZone = function(el, config){
18373     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18374     if(this.containerScroll){
18375         Roo.dd.ScrollManager.register(this.el);
18376     }
18377 };
18378
18379 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18380     /**
18381      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18382      * for auto scrolling during drag operations.
18383      */
18384     /**
18385      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18386      * method after a failed drop (defaults to "c3daf9" - light blue)
18387      */
18388
18389     /**
18390      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18391      * for a valid target to drag based on the mouse down. Override this method
18392      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18393      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18394      * @param {EventObject} e The mouse down event
18395      * @return {Object} The dragData
18396      */
18397     getDragData : function(e){
18398         return Roo.dd.Registry.getHandleFromEvent(e);
18399     },
18400     
18401     /**
18402      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18403      * this.dragData.ddel
18404      * @param {Number} x The x position of the click on the dragged object
18405      * @param {Number} y The y position of the click on the dragged object
18406      * @return {Boolean} true to continue the drag, false to cancel
18407      */
18408     onInitDrag : function(x, y){
18409         this.proxy.update(this.dragData.ddel.cloneNode(true));
18410         this.onStartDrag(x, y);
18411         return true;
18412     },
18413     
18414     /**
18415      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18416      */
18417     afterRepair : function(){
18418         if(Roo.enableFx){
18419             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18420         }
18421         this.dragging = false;
18422     },
18423
18424     /**
18425      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18426      * the XY of this.dragData.ddel
18427      * @param {EventObject} e The mouse up event
18428      * @return {Array} The xy location (e.g. [100, 200])
18429      */
18430     getRepairXY : function(e){
18431         return Roo.Element.fly(this.dragData.ddel).getXY();  
18432     }
18433 });/*
18434  * Based on:
18435  * Ext JS Library 1.1.1
18436  * Copyright(c) 2006-2007, Ext JS, LLC.
18437  *
18438  * Originally Released Under LGPL - original licence link has changed is not relivant.
18439  *
18440  * Fork - LGPL
18441  * <script type="text/javascript">
18442  */
18443 /**
18444  * @class Roo.dd.DropZone
18445  * @extends Roo.dd.DropTarget
18446  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18447  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18448  * @constructor
18449  * @param {String/HTMLElement/Element} el The container element
18450  * @param {Object} config
18451  */
18452 Roo.dd.DropZone = function(el, config){
18453     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18454 };
18455
18456 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18457     /**
18458      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18459      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18460      * provide your own custom lookup.
18461      * @param {Event} e The event
18462      * @return {Object} data The custom data
18463      */
18464     getTargetFromEvent : function(e){
18465         return Roo.dd.Registry.getTargetFromEvent(e);
18466     },
18467
18468     /**
18469      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18470      * that it has registered.  This method has no default implementation and should be overridden to provide
18471      * node-specific processing if necessary.
18472      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18473      * {@link #getTargetFromEvent} for this node)
18474      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18475      * @param {Event} e The event
18476      * @param {Object} data An object containing arbitrary data supplied by the drag source
18477      */
18478     onNodeEnter : function(n, dd, e, data){
18479         
18480     },
18481
18482     /**
18483      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18484      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18485      * overridden to provide the proper feedback.
18486      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18487      * {@link #getTargetFromEvent} for this node)
18488      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18489      * @param {Event} e The event
18490      * @param {Object} data An object containing arbitrary data supplied by the drag source
18491      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18492      * underlying {@link Roo.dd.StatusProxy} can be updated
18493      */
18494     onNodeOver : function(n, dd, e, data){
18495         return this.dropAllowed;
18496     },
18497
18498     /**
18499      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18500      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18501      * node-specific processing if necessary.
18502      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18503      * {@link #getTargetFromEvent} for this node)
18504      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18505      * @param {Event} e The event
18506      * @param {Object} data An object containing arbitrary data supplied by the drag source
18507      */
18508     onNodeOut : function(n, dd, e, data){
18509         
18510     },
18511
18512     /**
18513      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18514      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18515      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18516      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18517      * {@link #getTargetFromEvent} for this node)
18518      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18519      * @param {Event} e The event
18520      * @param {Object} data An object containing arbitrary data supplied by the drag source
18521      * @return {Boolean} True if the drop was valid, else false
18522      */
18523     onNodeDrop : function(n, dd, e, data){
18524         return false;
18525     },
18526
18527     /**
18528      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18529      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18530      * it should be overridden to provide the proper feedback if necessary.
18531      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18532      * @param {Event} e The event
18533      * @param {Object} data An object containing arbitrary data supplied by the drag source
18534      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18535      * underlying {@link Roo.dd.StatusProxy} can be updated
18536      */
18537     onContainerOver : function(dd, e, data){
18538         return this.dropNotAllowed;
18539     },
18540
18541     /**
18542      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18543      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18544      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18545      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18546      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18547      * @param {Event} e The event
18548      * @param {Object} data An object containing arbitrary data supplied by the drag source
18549      * @return {Boolean} True if the drop was valid, else false
18550      */
18551     onContainerDrop : function(dd, e, data){
18552         return false;
18553     },
18554
18555     /**
18556      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18557      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18558      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18559      * you should override this method and provide a custom implementation.
18560      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18561      * @param {Event} e The event
18562      * @param {Object} data An object containing arbitrary data supplied by the drag source
18563      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18564      * underlying {@link Roo.dd.StatusProxy} can be updated
18565      */
18566     notifyEnter : function(dd, e, data){
18567         return this.dropNotAllowed;
18568     },
18569
18570     /**
18571      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18572      * This method will be called on every mouse movement while the drag source is over the drop zone.
18573      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18574      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18575      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18576      * registered node, it will call {@link #onContainerOver}.
18577      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18578      * @param {Event} e The event
18579      * @param {Object} data An object containing arbitrary data supplied by the drag source
18580      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18581      * underlying {@link Roo.dd.StatusProxy} can be updated
18582      */
18583     notifyOver : function(dd, e, data){
18584         var n = this.getTargetFromEvent(e);
18585         if(!n){ // not over valid drop target
18586             if(this.lastOverNode){
18587                 this.onNodeOut(this.lastOverNode, dd, e, data);
18588                 this.lastOverNode = null;
18589             }
18590             return this.onContainerOver(dd, e, data);
18591         }
18592         if(this.lastOverNode != n){
18593             if(this.lastOverNode){
18594                 this.onNodeOut(this.lastOverNode, dd, e, data);
18595             }
18596             this.onNodeEnter(n, dd, e, data);
18597             this.lastOverNode = n;
18598         }
18599         return this.onNodeOver(n, dd, e, data);
18600     },
18601
18602     /**
18603      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18604      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18605      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18606      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18607      * @param {Event} e The event
18608      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18609      */
18610     notifyOut : function(dd, e, data){
18611         if(this.lastOverNode){
18612             this.onNodeOut(this.lastOverNode, dd, e, data);
18613             this.lastOverNode = null;
18614         }
18615     },
18616
18617     /**
18618      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18619      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18620      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18621      * otherwise it will call {@link #onContainerDrop}.
18622      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18623      * @param {Event} e The event
18624      * @param {Object} data An object containing arbitrary data supplied by the drag source
18625      * @return {Boolean} True if the drop was valid, else false
18626      */
18627     notifyDrop : function(dd, e, data){
18628         if(this.lastOverNode){
18629             this.onNodeOut(this.lastOverNode, dd, e, data);
18630             this.lastOverNode = null;
18631         }
18632         var n = this.getTargetFromEvent(e);
18633         return n ?
18634             this.onNodeDrop(n, dd, e, data) :
18635             this.onContainerDrop(dd, e, data);
18636     },
18637
18638     // private
18639     triggerCacheRefresh : function(){
18640         Roo.dd.DDM.refreshCache(this.groups);
18641     }  
18642 });/*
18643  * Based on:
18644  * Ext JS Library 1.1.1
18645  * Copyright(c) 2006-2007, Ext JS, LLC.
18646  *
18647  * Originally Released Under LGPL - original licence link has changed is not relivant.
18648  *
18649  * Fork - LGPL
18650  * <script type="text/javascript">
18651  */
18652
18653
18654 /**
18655  * @class Roo.data.SortTypes
18656  * @singleton
18657  * Defines the default sorting (casting?) comparison functions used when sorting data.
18658  */
18659 Roo.data.SortTypes = {
18660     /**
18661      * Default sort that does nothing
18662      * @param {Mixed} s The value being converted
18663      * @return {Mixed} The comparison value
18664      */
18665     none : function(s){
18666         return s;
18667     },
18668     
18669     /**
18670      * The regular expression used to strip tags
18671      * @type {RegExp}
18672      * @property
18673      */
18674     stripTagsRE : /<\/?[^>]+>/gi,
18675     
18676     /**
18677      * Strips all HTML tags to sort on text only
18678      * @param {Mixed} s The value being converted
18679      * @return {String} The comparison value
18680      */
18681     asText : function(s){
18682         return String(s).replace(this.stripTagsRE, "");
18683     },
18684     
18685     /**
18686      * Strips all HTML tags to sort on text only - Case insensitive
18687      * @param {Mixed} s The value being converted
18688      * @return {String} The comparison value
18689      */
18690     asUCText : function(s){
18691         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18692     },
18693     
18694     /**
18695      * Case insensitive string
18696      * @param {Mixed} s The value being converted
18697      * @return {String} The comparison value
18698      */
18699     asUCString : function(s) {
18700         return String(s).toUpperCase();
18701     },
18702     
18703     /**
18704      * Date sorting
18705      * @param {Mixed} s The value being converted
18706      * @return {Number} The comparison value
18707      */
18708     asDate : function(s) {
18709         if(!s){
18710             return 0;
18711         }
18712         if(s instanceof Date){
18713             return s.getTime();
18714         }
18715         return Date.parse(String(s));
18716     },
18717     
18718     /**
18719      * Float sorting
18720      * @param {Mixed} s The value being converted
18721      * @return {Float} The comparison value
18722      */
18723     asFloat : function(s) {
18724         var val = parseFloat(String(s).replace(/,/g, ""));
18725         if(isNaN(val)) val = 0;
18726         return val;
18727     },
18728     
18729     /**
18730      * Integer sorting
18731      * @param {Mixed} s The value being converted
18732      * @return {Number} The comparison value
18733      */
18734     asInt : function(s) {
18735         var val = parseInt(String(s).replace(/,/g, ""));
18736         if(isNaN(val)) val = 0;
18737         return val;
18738     }
18739 };/*
18740  * Based on:
18741  * Ext JS Library 1.1.1
18742  * Copyright(c) 2006-2007, Ext JS, LLC.
18743  *
18744  * Originally Released Under LGPL - original licence link has changed is not relivant.
18745  *
18746  * Fork - LGPL
18747  * <script type="text/javascript">
18748  */
18749
18750 /**
18751 * @class Roo.data.Record
18752  * Instances of this class encapsulate both record <em>definition</em> information, and record
18753  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18754  * to access Records cached in an {@link Roo.data.Store} object.<br>
18755  * <p>
18756  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18757  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18758  * objects.<br>
18759  * <p>
18760  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18761  * @constructor
18762  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18763  * {@link #create}. The parameters are the same.
18764  * @param {Array} data An associative Array of data values keyed by the field name.
18765  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18766  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18767  * not specified an integer id is generated.
18768  */
18769 Roo.data.Record = function(data, id){
18770     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18771     this.data = data;
18772 };
18773
18774 /**
18775  * Generate a constructor for a specific record layout.
18776  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18777  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18778  * Each field definition object may contain the following properties: <ul>
18779  * <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,
18780  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18781  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18782  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18783  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18784  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18785  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18786  * this may be omitted.</p></li>
18787  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18788  * <ul><li>auto (Default, implies no conversion)</li>
18789  * <li>string</li>
18790  * <li>int</li>
18791  * <li>float</li>
18792  * <li>boolean</li>
18793  * <li>date</li></ul></p></li>
18794  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18795  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18796  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18797  * by the Reader into an object that will be stored in the Record. It is passed the
18798  * following parameters:<ul>
18799  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18800  * </ul></p></li>
18801  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18802  * </ul>
18803  * <br>usage:<br><pre><code>
18804 var TopicRecord = Roo.data.Record.create(
18805     {name: 'title', mapping: 'topic_title'},
18806     {name: 'author', mapping: 'username'},
18807     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18808     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18809     {name: 'lastPoster', mapping: 'user2'},
18810     {name: 'excerpt', mapping: 'post_text'}
18811 );
18812
18813 var myNewRecord = new TopicRecord({
18814     title: 'Do my job please',
18815     author: 'noobie',
18816     totalPosts: 1,
18817     lastPost: new Date(),
18818     lastPoster: 'Animal',
18819     excerpt: 'No way dude!'
18820 });
18821 myStore.add(myNewRecord);
18822 </code></pre>
18823  * @method create
18824  * @static
18825  */
18826 Roo.data.Record.create = function(o){
18827     var f = function(){
18828         f.superclass.constructor.apply(this, arguments);
18829     };
18830     Roo.extend(f, Roo.data.Record);
18831     var p = f.prototype;
18832     p.fields = new Roo.util.MixedCollection(false, function(field){
18833         return field.name;
18834     });
18835     for(var i = 0, len = o.length; i < len; i++){
18836         p.fields.add(new Roo.data.Field(o[i]));
18837     }
18838     f.getField = function(name){
18839         return p.fields.get(name);  
18840     };
18841     return f;
18842 };
18843
18844 Roo.data.Record.AUTO_ID = 1000;
18845 Roo.data.Record.EDIT = 'edit';
18846 Roo.data.Record.REJECT = 'reject';
18847 Roo.data.Record.COMMIT = 'commit';
18848
18849 Roo.data.Record.prototype = {
18850     /**
18851      * Readonly flag - true if this record has been modified.
18852      * @type Boolean
18853      */
18854     dirty : false,
18855     editing : false,
18856     error: null,
18857     modified: null,
18858
18859     // private
18860     join : function(store){
18861         this.store = store;
18862     },
18863
18864     /**
18865      * Set the named field to the specified value.
18866      * @param {String} name The name of the field to set.
18867      * @param {Object} value The value to set the field to.
18868      */
18869     set : function(name, value){
18870         if(this.data[name] == value){
18871             return;
18872         }
18873         this.dirty = true;
18874         if(!this.modified){
18875             this.modified = {};
18876         }
18877         if(typeof this.modified[name] == 'undefined'){
18878             this.modified[name] = this.data[name];
18879         }
18880         this.data[name] = value;
18881         if(!this.editing){
18882             this.store.afterEdit(this);
18883         }       
18884     },
18885
18886     /**
18887      * Get the value of the named field.
18888      * @param {String} name The name of the field to get the value of.
18889      * @return {Object} The value of the field.
18890      */
18891     get : function(name){
18892         return this.data[name]; 
18893     },
18894
18895     // private
18896     beginEdit : function(){
18897         this.editing = true;
18898         this.modified = {}; 
18899     },
18900
18901     // private
18902     cancelEdit : function(){
18903         this.editing = false;
18904         delete this.modified;
18905     },
18906
18907     // private
18908     endEdit : function(){
18909         this.editing = false;
18910         if(this.dirty && this.store){
18911             this.store.afterEdit(this);
18912         }
18913     },
18914
18915     /**
18916      * Usually called by the {@link Roo.data.Store} which owns the Record.
18917      * Rejects all changes made to the Record since either creation, or the last commit operation.
18918      * Modified fields are reverted to their original values.
18919      * <p>
18920      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18921      * of reject operations.
18922      */
18923     reject : function(){
18924         var m = this.modified;
18925         for(var n in m){
18926             if(typeof m[n] != "function"){
18927                 this.data[n] = m[n];
18928             }
18929         }
18930         this.dirty = false;
18931         delete this.modified;
18932         this.editing = false;
18933         if(this.store){
18934             this.store.afterReject(this);
18935         }
18936     },
18937
18938     /**
18939      * Usually called by the {@link Roo.data.Store} which owns the Record.
18940      * Commits all changes made to the Record since either creation, or the last commit operation.
18941      * <p>
18942      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18943      * of commit operations.
18944      */
18945     commit : function(){
18946         this.dirty = false;
18947         delete this.modified;
18948         this.editing = false;
18949         if(this.store){
18950             this.store.afterCommit(this);
18951         }
18952     },
18953
18954     // private
18955     hasError : function(){
18956         return this.error != null;
18957     },
18958
18959     // private
18960     clearError : function(){
18961         this.error = null;
18962     },
18963
18964     /**
18965      * Creates a copy of this record.
18966      * @param {String} id (optional) A new record id if you don't want to use this record's id
18967      * @return {Record}
18968      */
18969     copy : function(newId) {
18970         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18971     }
18972 };/*
18973  * Based on:
18974  * Ext JS Library 1.1.1
18975  * Copyright(c) 2006-2007, Ext JS, LLC.
18976  *
18977  * Originally Released Under LGPL - original licence link has changed is not relivant.
18978  *
18979  * Fork - LGPL
18980  * <script type="text/javascript">
18981  */
18982
18983
18984
18985 /**
18986  * @class Roo.data.Store
18987  * @extends Roo.util.Observable
18988  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18989  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18990  * <p>
18991  * 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
18992  * has no knowledge of the format of the data returned by the Proxy.<br>
18993  * <p>
18994  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18995  * instances from the data object. These records are cached and made available through accessor functions.
18996  * @constructor
18997  * Creates a new Store.
18998  * @param {Object} config A config object containing the objects needed for the Store to access data,
18999  * and read the data into Records.
19000  */
19001 Roo.data.Store = function(config){
19002     this.data = new Roo.util.MixedCollection(false);
19003     this.data.getKey = function(o){
19004         return o.id;
19005     };
19006     this.baseParams = {};
19007     // private
19008     this.paramNames = {
19009         "start" : "start",
19010         "limit" : "limit",
19011         "sort" : "sort",
19012         "dir" : "dir"
19013     };
19014
19015     if(config && config.data){
19016         this.inlineData = config.data;
19017         delete config.data;
19018     }
19019
19020     Roo.apply(this, config);
19021     
19022     if(this.reader){ // reader passed
19023         this.reader = Roo.factory(this.reader, Roo.data);
19024         this.reader.xmodule = this.xmodule || false;
19025         if(!this.recordType){
19026             this.recordType = this.reader.recordType;
19027         }
19028         if(this.reader.onMetaChange){
19029             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19030         }
19031     }
19032
19033     if(this.recordType){
19034         this.fields = this.recordType.prototype.fields;
19035     }
19036     this.modified = [];
19037
19038     this.addEvents({
19039         /**
19040          * @event datachanged
19041          * Fires when the data cache has changed, and a widget which is using this Store
19042          * as a Record cache should refresh its view.
19043          * @param {Store} this
19044          */
19045         datachanged : true,
19046         /**
19047          * @event metachange
19048          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19049          * @param {Store} this
19050          * @param {Object} meta The JSON metadata
19051          */
19052         metachange : true,
19053         /**
19054          * @event add
19055          * Fires when Records have been added to the Store
19056          * @param {Store} this
19057          * @param {Roo.data.Record[]} records The array of Records added
19058          * @param {Number} index The index at which the record(s) were added
19059          */
19060         add : true,
19061         /**
19062          * @event remove
19063          * Fires when a Record has been removed from the Store
19064          * @param {Store} this
19065          * @param {Roo.data.Record} record The Record that was removed
19066          * @param {Number} index The index at which the record was removed
19067          */
19068         remove : true,
19069         /**
19070          * @event update
19071          * Fires when a Record has been updated
19072          * @param {Store} this
19073          * @param {Roo.data.Record} record The Record that was updated
19074          * @param {String} operation The update operation being performed.  Value may be one of:
19075          * <pre><code>
19076  Roo.data.Record.EDIT
19077  Roo.data.Record.REJECT
19078  Roo.data.Record.COMMIT
19079          * </code></pre>
19080          */
19081         update : true,
19082         /**
19083          * @event clear
19084          * Fires when the data cache has been cleared.
19085          * @param {Store} this
19086          */
19087         clear : true,
19088         /**
19089          * @event beforeload
19090          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19091          * the load action will be canceled.
19092          * @param {Store} this
19093          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19094          */
19095         beforeload : true,
19096         /**
19097          * @event load
19098          * Fires after a new set of Records has been loaded.
19099          * @param {Store} this
19100          * @param {Roo.data.Record[]} records The Records that were loaded
19101          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19102          */
19103         load : true,
19104         /**
19105          * @event loadexception
19106          * Fires if an exception occurs in the Proxy during loading.
19107          * Called with the signature of the Proxy's "loadexception" event.
19108          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19109          * 
19110          * @param {Proxy} 
19111          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19112          * @param {Object} load options 
19113          * @param {Object} jsonData from your request (normally this contains the Exception)
19114          */
19115         loadexception : true
19116     });
19117     
19118     if(this.proxy){
19119         this.proxy = Roo.factory(this.proxy, Roo.data);
19120         this.proxy.xmodule = this.xmodule || false;
19121         this.relayEvents(this.proxy,  ["loadexception"]);
19122     }
19123     this.sortToggle = {};
19124
19125     Roo.data.Store.superclass.constructor.call(this);
19126
19127     if(this.inlineData){
19128         this.loadData(this.inlineData);
19129         delete this.inlineData;
19130     }
19131 };
19132 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19133      /**
19134     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19135     * without a remote query - used by combo/forms at present.
19136     */
19137     
19138     /**
19139     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19140     */
19141     /**
19142     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19143     */
19144     /**
19145     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19146     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19147     */
19148     /**
19149     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19150     * on any HTTP request
19151     */
19152     /**
19153     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19154     */
19155     /**
19156     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19157     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19158     */
19159     remoteSort : false,
19160
19161     /**
19162     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19163      * loaded or when a record is removed. (defaults to false).
19164     */
19165     pruneModifiedRecords : false,
19166
19167     // private
19168     lastOptions : null,
19169
19170     /**
19171      * Add Records to the Store and fires the add event.
19172      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19173      */
19174     add : function(records){
19175         records = [].concat(records);
19176         for(var i = 0, len = records.length; i < len; i++){
19177             records[i].join(this);
19178         }
19179         var index = this.data.length;
19180         this.data.addAll(records);
19181         this.fireEvent("add", this, records, index);
19182     },
19183
19184     /**
19185      * Remove a Record from the Store and fires the remove event.
19186      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19187      */
19188     remove : function(record){
19189         var index = this.data.indexOf(record);
19190         this.data.removeAt(index);
19191         if(this.pruneModifiedRecords){
19192             this.modified.remove(record);
19193         }
19194         this.fireEvent("remove", this, record, index);
19195     },
19196
19197     /**
19198      * Remove all Records from the Store and fires the clear event.
19199      */
19200     removeAll : function(){
19201         this.data.clear();
19202         if(this.pruneModifiedRecords){
19203             this.modified = [];
19204         }
19205         this.fireEvent("clear", this);
19206     },
19207
19208     /**
19209      * Inserts Records to the Store at the given index and fires the add event.
19210      * @param {Number} index The start index at which to insert the passed Records.
19211      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19212      */
19213     insert : function(index, records){
19214         records = [].concat(records);
19215         for(var i = 0, len = records.length; i < len; i++){
19216             this.data.insert(index, records[i]);
19217             records[i].join(this);
19218         }
19219         this.fireEvent("add", this, records, index);
19220     },
19221
19222     /**
19223      * Get the index within the cache of the passed Record.
19224      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19225      * @return {Number} The index of the passed Record. Returns -1 if not found.
19226      */
19227     indexOf : function(record){
19228         return this.data.indexOf(record);
19229     },
19230
19231     /**
19232      * Get the index within the cache of the Record with the passed id.
19233      * @param {String} id The id of the Record to find.
19234      * @return {Number} The index of the Record. Returns -1 if not found.
19235      */
19236     indexOfId : function(id){
19237         return this.data.indexOfKey(id);
19238     },
19239
19240     /**
19241      * Get the Record with the specified id.
19242      * @param {String} id The id of the Record to find.
19243      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19244      */
19245     getById : function(id){
19246         return this.data.key(id);
19247     },
19248
19249     /**
19250      * Get the Record at the specified index.
19251      * @param {Number} index The index of the Record to find.
19252      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19253      */
19254     getAt : function(index){
19255         return this.data.itemAt(index);
19256     },
19257
19258     /**
19259      * Returns a range of Records between specified indices.
19260      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19261      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19262      * @return {Roo.data.Record[]} An array of Records
19263      */
19264     getRange : function(start, end){
19265         return this.data.getRange(start, end);
19266     },
19267
19268     // private
19269     storeOptions : function(o){
19270         o = Roo.apply({}, o);
19271         delete o.callback;
19272         delete o.scope;
19273         this.lastOptions = o;
19274     },
19275
19276     /**
19277      * Loads the Record cache from the configured Proxy using the configured Reader.
19278      * <p>
19279      * If using remote paging, then the first load call must specify the <em>start</em>
19280      * and <em>limit</em> properties in the options.params property to establish the initial
19281      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19282      * <p>
19283      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19284      * and this call will return before the new data has been loaded. Perform any post-processing
19285      * in a callback function, or in a "load" event handler.</strong>
19286      * <p>
19287      * @param {Object} options An object containing properties which control loading options:<ul>
19288      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19289      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19290      * passed the following arguments:<ul>
19291      * <li>r : Roo.data.Record[]</li>
19292      * <li>options: Options object from the load call</li>
19293      * <li>success: Boolean success indicator</li></ul></li>
19294      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19295      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19296      * </ul>
19297      */
19298     load : function(options){
19299         options = options || {};
19300         if(this.fireEvent("beforeload", this, options) !== false){
19301             this.storeOptions(options);
19302             var p = Roo.apply(options.params || {}, this.baseParams);
19303             // if meta was not loaded from remote source.. try requesting it.
19304             if (!this.reader.metaFromRemote) {
19305                 p._requestMeta = 1;
19306             }
19307             if(this.sortInfo && this.remoteSort){
19308                 var pn = this.paramNames;
19309                 p[pn["sort"]] = this.sortInfo.field;
19310                 p[pn["dir"]] = this.sortInfo.direction;
19311             }
19312             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19313         }
19314     },
19315
19316     /**
19317      * Reloads the Record cache from the configured Proxy using the configured Reader and
19318      * the options from the last load operation performed.
19319      * @param {Object} options (optional) An object containing properties which may override the options
19320      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19321      * the most recently used options are reused).
19322      */
19323     reload : function(options){
19324         this.load(Roo.applyIf(options||{}, this.lastOptions));
19325     },
19326
19327     // private
19328     // Called as a callback by the Reader during a load operation.
19329     loadRecords : function(o, options, success){
19330         if(!o || success === false){
19331             if(success !== false){
19332                 this.fireEvent("load", this, [], options);
19333             }
19334             if(options.callback){
19335                 options.callback.call(options.scope || this, [], options, false);
19336             }
19337             return;
19338         }
19339         // if data returned failure - throw an exception.
19340         if (o.success === false) {
19341             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19342             return;
19343         }
19344         var r = o.records, t = o.totalRecords || r.length;
19345         if(!options || options.add !== true){
19346             if(this.pruneModifiedRecords){
19347                 this.modified = [];
19348             }
19349             for(var i = 0, len = r.length; i < len; i++){
19350                 r[i].join(this);
19351             }
19352             if(this.snapshot){
19353                 this.data = this.snapshot;
19354                 delete this.snapshot;
19355             }
19356             this.data.clear();
19357             this.data.addAll(r);
19358             this.totalLength = t;
19359             this.applySort();
19360             this.fireEvent("datachanged", this);
19361         }else{
19362             this.totalLength = Math.max(t, this.data.length+r.length);
19363             this.add(r);
19364         }
19365         this.fireEvent("load", this, r, options);
19366         if(options.callback){
19367             options.callback.call(options.scope || this, r, options, true);
19368         }
19369     },
19370
19371     /**
19372      * Loads data from a passed data block. A Reader which understands the format of the data
19373      * must have been configured in the constructor.
19374      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19375      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19376      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19377      */
19378     loadData : function(o, append){
19379         var r = this.reader.readRecords(o);
19380         this.loadRecords(r, {add: append}, true);
19381     },
19382
19383     /**
19384      * Gets the number of cached records.
19385      * <p>
19386      * <em>If using paging, this may not be the total size of the dataset. If the data object
19387      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19388      * the data set size</em>
19389      */
19390     getCount : function(){
19391         return this.data.length || 0;
19392     },
19393
19394     /**
19395      * Gets the total number of records in the dataset as returned by the server.
19396      * <p>
19397      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19398      * the dataset size</em>
19399      */
19400     getTotalCount : function(){
19401         return this.totalLength || 0;
19402     },
19403
19404     /**
19405      * Returns the sort state of the Store as an object with two properties:
19406      * <pre><code>
19407  field {String} The name of the field by which the Records are sorted
19408  direction {String} The sort order, "ASC" or "DESC"
19409      * </code></pre>
19410      */
19411     getSortState : function(){
19412         return this.sortInfo;
19413     },
19414
19415     // private
19416     applySort : function(){
19417         if(this.sortInfo && !this.remoteSort){
19418             var s = this.sortInfo, f = s.field;
19419             var st = this.fields.get(f).sortType;
19420             var fn = function(r1, r2){
19421                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19422                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19423             };
19424             this.data.sort(s.direction, fn);
19425             if(this.snapshot && this.snapshot != this.data){
19426                 this.snapshot.sort(s.direction, fn);
19427             }
19428         }
19429     },
19430
19431     /**
19432      * Sets the default sort column and order to be used by the next load operation.
19433      * @param {String} fieldName The name of the field to sort by.
19434      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19435      */
19436     setDefaultSort : function(field, dir){
19437         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19438     },
19439
19440     /**
19441      * Sort the Records.
19442      * If remote sorting is used, the sort is performed on the server, and the cache is
19443      * reloaded. If local sorting is used, the cache is sorted internally.
19444      * @param {String} fieldName The name of the field to sort by.
19445      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19446      */
19447     sort : function(fieldName, dir){
19448         var f = this.fields.get(fieldName);
19449         if(!dir){
19450             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19451                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19452             }else{
19453                 dir = f.sortDir;
19454             }
19455         }
19456         this.sortToggle[f.name] = dir;
19457         this.sortInfo = {field: f.name, direction: dir};
19458         if(!this.remoteSort){
19459             this.applySort();
19460             this.fireEvent("datachanged", this);
19461         }else{
19462             this.load(this.lastOptions);
19463         }
19464     },
19465
19466     /**
19467      * Calls the specified function for each of the Records in the cache.
19468      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19469      * Returning <em>false</em> aborts and exits the iteration.
19470      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19471      */
19472     each : function(fn, scope){
19473         this.data.each(fn, scope);
19474     },
19475
19476     /**
19477      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19478      * (e.g., during paging).
19479      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19480      */
19481     getModifiedRecords : function(){
19482         return this.modified;
19483     },
19484
19485     // private
19486     createFilterFn : function(property, value, anyMatch){
19487         if(!value.exec){ // not a regex
19488             value = String(value);
19489             if(value.length == 0){
19490                 return false;
19491             }
19492             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19493         }
19494         return function(r){
19495             return value.test(r.data[property]);
19496         };
19497     },
19498
19499     /**
19500      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19501      * @param {String} property A field on your records
19502      * @param {Number} start The record index to start at (defaults to 0)
19503      * @param {Number} end The last record index to include (defaults to length - 1)
19504      * @return {Number} The sum
19505      */
19506     sum : function(property, start, end){
19507         var rs = this.data.items, v = 0;
19508         start = start || 0;
19509         end = (end || end === 0) ? end : rs.length-1;
19510
19511         for(var i = start; i <= end; i++){
19512             v += (rs[i].data[property] || 0);
19513         }
19514         return v;
19515     },
19516
19517     /**
19518      * Filter the records by a specified property.
19519      * @param {String} field A field on your records
19520      * @param {String/RegExp} value Either a string that the field
19521      * should start with or a RegExp to test against the field
19522      * @param {Boolean} anyMatch True to match any part not just the beginning
19523      */
19524     filter : function(property, value, anyMatch){
19525         var fn = this.createFilterFn(property, value, anyMatch);
19526         return fn ? this.filterBy(fn) : this.clearFilter();
19527     },
19528
19529     /**
19530      * Filter by a function. The specified function will be called with each
19531      * record in this data source. If the function returns true the record is included,
19532      * otherwise it is filtered.
19533      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19534      * @param {Object} scope (optional) The scope of the function (defaults to this)
19535      */
19536     filterBy : function(fn, scope){
19537         this.snapshot = this.snapshot || this.data;
19538         this.data = this.queryBy(fn, scope||this);
19539         this.fireEvent("datachanged", this);
19540     },
19541
19542     /**
19543      * Query the records by a specified property.
19544      * @param {String} field A field on your records
19545      * @param {String/RegExp} value Either a string that the field
19546      * should start with or a RegExp to test against the field
19547      * @param {Boolean} anyMatch True to match any part not just the beginning
19548      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19549      */
19550     query : function(property, value, anyMatch){
19551         var fn = this.createFilterFn(property, value, anyMatch);
19552         return fn ? this.queryBy(fn) : this.data.clone();
19553     },
19554
19555     /**
19556      * Query by a function. The specified function will be called with each
19557      * record in this data source. If the function returns true the record is included
19558      * in the results.
19559      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19560      * @param {Object} scope (optional) The scope of the function (defaults to this)
19561       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19562      **/
19563     queryBy : function(fn, scope){
19564         var data = this.snapshot || this.data;
19565         return data.filterBy(fn, scope||this);
19566     },
19567
19568     /**
19569      * Collects unique values for a particular dataIndex from this store.
19570      * @param {String} dataIndex The property to collect
19571      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19572      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19573      * @return {Array} An array of the unique values
19574      **/
19575     collect : function(dataIndex, allowNull, bypassFilter){
19576         var d = (bypassFilter === true && this.snapshot) ?
19577                 this.snapshot.items : this.data.items;
19578         var v, sv, r = [], l = {};
19579         for(var i = 0, len = d.length; i < len; i++){
19580             v = d[i].data[dataIndex];
19581             sv = String(v);
19582             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19583                 l[sv] = true;
19584                 r[r.length] = v;
19585             }
19586         }
19587         return r;
19588     },
19589
19590     /**
19591      * Revert to a view of the Record cache with no filtering applied.
19592      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19593      */
19594     clearFilter : function(suppressEvent){
19595         if(this.snapshot && this.snapshot != this.data){
19596             this.data = this.snapshot;
19597             delete this.snapshot;
19598             if(suppressEvent !== true){
19599                 this.fireEvent("datachanged", this);
19600             }
19601         }
19602     },
19603
19604     // private
19605     afterEdit : function(record){
19606         if(this.modified.indexOf(record) == -1){
19607             this.modified.push(record);
19608         }
19609         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19610     },
19611
19612     // private
19613     afterReject : function(record){
19614         this.modified.remove(record);
19615         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19616     },
19617
19618     // private
19619     afterCommit : function(record){
19620         this.modified.remove(record);
19621         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19622     },
19623
19624     /**
19625      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19626      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19627      */
19628     commitChanges : function(){
19629         var m = this.modified.slice(0);
19630         this.modified = [];
19631         for(var i = 0, len = m.length; i < len; i++){
19632             m[i].commit();
19633         }
19634     },
19635
19636     /**
19637      * Cancel outstanding changes on all changed records.
19638      */
19639     rejectChanges : function(){
19640         var m = this.modified.slice(0);
19641         this.modified = [];
19642         for(var i = 0, len = m.length; i < len; i++){
19643             m[i].reject();
19644         }
19645     },
19646
19647     onMetaChange : function(meta, rtype, o){
19648         this.recordType = rtype;
19649         this.fields = rtype.prototype.fields;
19650         delete this.snapshot;
19651         this.sortInfo = meta.sortInfo || this.sortInfo;
19652         this.modified = [];
19653         this.fireEvent('metachange', this, this.reader.meta);
19654     }
19655 });/*
19656  * Based on:
19657  * Ext JS Library 1.1.1
19658  * Copyright(c) 2006-2007, Ext JS, LLC.
19659  *
19660  * Originally Released Under LGPL - original licence link has changed is not relivant.
19661  *
19662  * Fork - LGPL
19663  * <script type="text/javascript">
19664  */
19665
19666 /**
19667  * @class Roo.data.SimpleStore
19668  * @extends Roo.data.Store
19669  * Small helper class to make creating Stores from Array data easier.
19670  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19671  * @cfg {Array} fields An array of field definition objects, or field name strings.
19672  * @cfg {Array} data The multi-dimensional array of data
19673  * @constructor
19674  * @param {Object} config
19675  */
19676 Roo.data.SimpleStore = function(config){
19677     Roo.data.SimpleStore.superclass.constructor.call(this, {
19678         isLocal : true,
19679         reader: new Roo.data.ArrayReader({
19680                 id: config.id
19681             },
19682             Roo.data.Record.create(config.fields)
19683         ),
19684         proxy : new Roo.data.MemoryProxy(config.data)
19685     });
19686     this.load();
19687 };
19688 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19689  * Based on:
19690  * Ext JS Library 1.1.1
19691  * Copyright(c) 2006-2007, Ext JS, LLC.
19692  *
19693  * Originally Released Under LGPL - original licence link has changed is not relivant.
19694  *
19695  * Fork - LGPL
19696  * <script type="text/javascript">
19697  */
19698
19699 /**
19700 /**
19701  * @extends Roo.data.Store
19702  * @class Roo.data.JsonStore
19703  * Small helper class to make creating Stores for JSON data easier. <br/>
19704 <pre><code>
19705 var store = new Roo.data.JsonStore({
19706     url: 'get-images.php',
19707     root: 'images',
19708     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19709 });
19710 </code></pre>
19711  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19712  * JsonReader and HttpProxy (unless inline data is provided).</b>
19713  * @cfg {Array} fields An array of field definition objects, or field name strings.
19714  * @constructor
19715  * @param {Object} config
19716  */
19717 Roo.data.JsonStore = function(c){
19718     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19719         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19720         reader: new Roo.data.JsonReader(c, c.fields)
19721     }));
19722 };
19723 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19724  * Based on:
19725  * Ext JS Library 1.1.1
19726  * Copyright(c) 2006-2007, Ext JS, LLC.
19727  *
19728  * Originally Released Under LGPL - original licence link has changed is not relivant.
19729  *
19730  * Fork - LGPL
19731  * <script type="text/javascript">
19732  */
19733
19734  
19735 Roo.data.Field = function(config){
19736     if(typeof config == "string"){
19737         config = {name: config};
19738     }
19739     Roo.apply(this, config);
19740     
19741     if(!this.type){
19742         this.type = "auto";
19743     }
19744     
19745     var st = Roo.data.SortTypes;
19746     // named sortTypes are supported, here we look them up
19747     if(typeof this.sortType == "string"){
19748         this.sortType = st[this.sortType];
19749     }
19750     
19751     // set default sortType for strings and dates
19752     if(!this.sortType){
19753         switch(this.type){
19754             case "string":
19755                 this.sortType = st.asUCString;
19756                 break;
19757             case "date":
19758                 this.sortType = st.asDate;
19759                 break;
19760             default:
19761                 this.sortType = st.none;
19762         }
19763     }
19764
19765     // define once
19766     var stripRe = /[\$,%]/g;
19767
19768     // prebuilt conversion function for this field, instead of
19769     // switching every time we're reading a value
19770     if(!this.convert){
19771         var cv, dateFormat = this.dateFormat;
19772         switch(this.type){
19773             case "":
19774             case "auto":
19775             case undefined:
19776                 cv = function(v){ return v; };
19777                 break;
19778             case "string":
19779                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19780                 break;
19781             case "int":
19782                 cv = function(v){
19783                     return v !== undefined && v !== null && v !== '' ?
19784                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19785                     };
19786                 break;
19787             case "float":
19788                 cv = function(v){
19789                     return v !== undefined && v !== null && v !== '' ?
19790                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19791                     };
19792                 break;
19793             case "bool":
19794             case "boolean":
19795                 cv = function(v){ return v === true || v === "true" || v == 1; };
19796                 break;
19797             case "date":
19798                 cv = function(v){
19799                     if(!v){
19800                         return '';
19801                     }
19802                     if(v instanceof Date){
19803                         return v;
19804                     }
19805                     if(dateFormat){
19806                         if(dateFormat == "timestamp"){
19807                             return new Date(v*1000);
19808                         }
19809                         return Date.parseDate(v, dateFormat);
19810                     }
19811                     var parsed = Date.parse(v);
19812                     return parsed ? new Date(parsed) : null;
19813                 };
19814              break;
19815             
19816         }
19817         this.convert = cv;
19818     }
19819 };
19820
19821 Roo.data.Field.prototype = {
19822     dateFormat: null,
19823     defaultValue: "",
19824     mapping: null,
19825     sortType : null,
19826     sortDir : "ASC"
19827 };/*
19828  * Based on:
19829  * Ext JS Library 1.1.1
19830  * Copyright(c) 2006-2007, Ext JS, LLC.
19831  *
19832  * Originally Released Under LGPL - original licence link has changed is not relivant.
19833  *
19834  * Fork - LGPL
19835  * <script type="text/javascript">
19836  */
19837  
19838 // Base class for reading structured data from a data source.  This class is intended to be
19839 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19840
19841 /**
19842  * @class Roo.data.DataReader
19843  * Base class for reading structured data from a data source.  This class is intended to be
19844  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19845  */
19846
19847 Roo.data.DataReader = function(meta, recordType){
19848     
19849     this.meta = meta;
19850     
19851     this.recordType = recordType instanceof Array ? 
19852         Roo.data.Record.create(recordType) : recordType;
19853 };
19854
19855 Roo.data.DataReader.prototype = {
19856      /**
19857      * Create an empty record
19858      * @param {Object} data (optional) - overlay some values
19859      * @return {Roo.data.Record} record created.
19860      */
19861     newRow :  function(d) {
19862         var da =  {};
19863         this.recordType.prototype.fields.each(function(c) {
19864             switch( c.type) {
19865                 case 'int' : da[c.name] = 0; break;
19866                 case 'date' : da[c.name] = new Date(); break;
19867                 case 'float' : da[c.name] = 0.0; break;
19868                 case 'boolean' : da[c.name] = false; break;
19869                 default : da[c.name] = ""; break;
19870             }
19871             
19872         });
19873         return new this.recordType(Roo.apply(da, d));
19874     }
19875     
19876 };/*
19877  * Based on:
19878  * Ext JS Library 1.1.1
19879  * Copyright(c) 2006-2007, Ext JS, LLC.
19880  *
19881  * Originally Released Under LGPL - original licence link has changed is not relivant.
19882  *
19883  * Fork - LGPL
19884  * <script type="text/javascript">
19885  */
19886
19887 /**
19888  * @class Roo.data.DataProxy
19889  * @extends Roo.data.Observable
19890  * This class is an abstract base class for implementations which provide retrieval of
19891  * unformatted data objects.<br>
19892  * <p>
19893  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19894  * (of the appropriate type which knows how to parse the data object) to provide a block of
19895  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19896  * <p>
19897  * Custom implementations must implement the load method as described in
19898  * {@link Roo.data.HttpProxy#load}.
19899  */
19900 Roo.data.DataProxy = function(){
19901     this.addEvents({
19902         /**
19903          * @event beforeload
19904          * Fires before a network request is made to retrieve a data object.
19905          * @param {Object} This DataProxy object.
19906          * @param {Object} params The params parameter to the load function.
19907          */
19908         beforeload : true,
19909         /**
19910          * @event load
19911          * Fires before the load method's callback is called.
19912          * @param {Object} This DataProxy object.
19913          * @param {Object} o The data object.
19914          * @param {Object} arg The callback argument object passed to the load function.
19915          */
19916         load : true,
19917         /**
19918          * @event loadexception
19919          * Fires if an Exception occurs during data retrieval.
19920          * @param {Object} This DataProxy object.
19921          * @param {Object} o The data object.
19922          * @param {Object} arg The callback argument object passed to the load function.
19923          * @param {Object} e The Exception.
19924          */
19925         loadexception : true
19926     });
19927     Roo.data.DataProxy.superclass.constructor.call(this);
19928 };
19929
19930 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19931
19932     /**
19933      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19934      */
19935 /*
19936  * Based on:
19937  * Ext JS Library 1.1.1
19938  * Copyright(c) 2006-2007, Ext JS, LLC.
19939  *
19940  * Originally Released Under LGPL - original licence link has changed is not relivant.
19941  *
19942  * Fork - LGPL
19943  * <script type="text/javascript">
19944  */
19945 /**
19946  * @class Roo.data.MemoryProxy
19947  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19948  * to the Reader when its load method is called.
19949  * @constructor
19950  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19951  */
19952 Roo.data.MemoryProxy = function(data){
19953     if (data.data) {
19954         data = data.data;
19955     }
19956     Roo.data.MemoryProxy.superclass.constructor.call(this);
19957     this.data = data;
19958 };
19959
19960 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19961     /**
19962      * Load data from the requested source (in this case an in-memory
19963      * data object passed to the constructor), read the data object into
19964      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19965      * process that block using the passed callback.
19966      * @param {Object} params This parameter is not used by the MemoryProxy class.
19967      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19968      * object into a block of Roo.data.Records.
19969      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19970      * The function must be passed <ul>
19971      * <li>The Record block object</li>
19972      * <li>The "arg" argument from the load function</li>
19973      * <li>A boolean success indicator</li>
19974      * </ul>
19975      * @param {Object} scope The scope in which to call the callback
19976      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19977      */
19978     load : function(params, reader, callback, scope, arg){
19979         params = params || {};
19980         var result;
19981         try {
19982             result = reader.readRecords(this.data);
19983         }catch(e){
19984             this.fireEvent("loadexception", this, arg, null, e);
19985             callback.call(scope, null, arg, false);
19986             return;
19987         }
19988         callback.call(scope, result, arg, true);
19989     },
19990     
19991     // private
19992     update : function(params, records){
19993         
19994     }
19995 });/*
19996  * Based on:
19997  * Ext JS Library 1.1.1
19998  * Copyright(c) 2006-2007, Ext JS, LLC.
19999  *
20000  * Originally Released Under LGPL - original licence link has changed is not relivant.
20001  *
20002  * Fork - LGPL
20003  * <script type="text/javascript">
20004  */
20005 /**
20006  * @class Roo.data.HttpProxy
20007  * @extends Roo.data.DataProxy
20008  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20009  * configured to reference a certain URL.<br><br>
20010  * <p>
20011  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20012  * from which the running page was served.<br><br>
20013  * <p>
20014  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20015  * <p>
20016  * Be aware that to enable the browser to parse an XML document, the server must set
20017  * the Content-Type header in the HTTP response to "text/xml".
20018  * @constructor
20019  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20020  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20021  * will be used to make the request.
20022  */
20023 Roo.data.HttpProxy = function(conn){
20024     Roo.data.HttpProxy.superclass.constructor.call(this);
20025     // is conn a conn config or a real conn?
20026     this.conn = conn;
20027     this.useAjax = !conn || !conn.events;
20028   
20029 };
20030
20031 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20032     // thse are take from connection...
20033     
20034     /**
20035      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20036      */
20037     /**
20038      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20039      * extra parameters to each request made by this object. (defaults to undefined)
20040      */
20041     /**
20042      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20043      *  to each request made by this object. (defaults to undefined)
20044      */
20045     /**
20046      * @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)
20047      */
20048     /**
20049      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20050      */
20051      /**
20052      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20053      * @type Boolean
20054      */
20055   
20056
20057     /**
20058      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20059      * @type Boolean
20060      */
20061     /**
20062      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20063      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20064      * a finer-grained basis than the DataProxy events.
20065      */
20066     getConnection : function(){
20067         return this.useAjax ? Roo.Ajax : this.conn;
20068     },
20069
20070     /**
20071      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20072      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20073      * process that block using the passed callback.
20074      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20075      * for the request to the remote server.
20076      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20077      * object into a block of Roo.data.Records.
20078      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20079      * The function must be passed <ul>
20080      * <li>The Record block object</li>
20081      * <li>The "arg" argument from the load function</li>
20082      * <li>A boolean success indicator</li>
20083      * </ul>
20084      * @param {Object} scope The scope in which to call the callback
20085      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20086      */
20087     load : function(params, reader, callback, scope, arg){
20088         if(this.fireEvent("beforeload", this, params) !== false){
20089             var  o = {
20090                 params : params || {},
20091                 request: {
20092                     callback : callback,
20093                     scope : scope,
20094                     arg : arg
20095                 },
20096                 reader: reader,
20097                 callback : this.loadResponse,
20098                 scope: this
20099             };
20100             if(this.useAjax){
20101                 Roo.applyIf(o, this.conn);
20102                 if(this.activeRequest){
20103                     Roo.Ajax.abort(this.activeRequest);
20104                 }
20105                 this.activeRequest = Roo.Ajax.request(o);
20106             }else{
20107                 this.conn.request(o);
20108             }
20109         }else{
20110             callback.call(scope||this, null, arg, false);
20111         }
20112     },
20113
20114     // private
20115     loadResponse : function(o, success, response){
20116         delete this.activeRequest;
20117         if(!success){
20118             this.fireEvent("loadexception", this, o, response);
20119             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20120             return;
20121         }
20122         var result;
20123         try {
20124             result = o.reader.read(response);
20125         }catch(e){
20126             this.fireEvent("loadexception", this, o, response, e);
20127             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20128             return;
20129         }
20130         
20131         this.fireEvent("load", this, o, o.request.arg);
20132         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20133     },
20134
20135     // private
20136     update : function(dataSet){
20137
20138     },
20139
20140     // private
20141     updateResponse : function(dataSet){
20142
20143     }
20144 });/*
20145  * Based on:
20146  * Ext JS Library 1.1.1
20147  * Copyright(c) 2006-2007, Ext JS, LLC.
20148  *
20149  * Originally Released Under LGPL - original licence link has changed is not relivant.
20150  *
20151  * Fork - LGPL
20152  * <script type="text/javascript">
20153  */
20154
20155 /**
20156  * @class Roo.data.ScriptTagProxy
20157  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20158  * other than the originating domain of the running page.<br><br>
20159  * <p>
20160  * <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
20161  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20162  * <p>
20163  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20164  * source code that is used as the source inside a &lt;script> tag.<br><br>
20165  * <p>
20166  * In order for the browser to process the returned data, the server must wrap the data object
20167  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20168  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20169  * depending on whether the callback name was passed:
20170  * <p>
20171  * <pre><code>
20172 boolean scriptTag = false;
20173 String cb = request.getParameter("callback");
20174 if (cb != null) {
20175     scriptTag = true;
20176     response.setContentType("text/javascript");
20177 } else {
20178     response.setContentType("application/x-json");
20179 }
20180 Writer out = response.getWriter();
20181 if (scriptTag) {
20182     out.write(cb + "(");
20183 }
20184 out.print(dataBlock.toJsonString());
20185 if (scriptTag) {
20186     out.write(");");
20187 }
20188 </pre></code>
20189  *
20190  * @constructor
20191  * @param {Object} config A configuration object.
20192  */
20193 Roo.data.ScriptTagProxy = function(config){
20194     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20195     Roo.apply(this, config);
20196     this.head = document.getElementsByTagName("head")[0];
20197 };
20198
20199 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20200
20201 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20202     /**
20203      * @cfg {String} url The URL from which to request the data object.
20204      */
20205     /**
20206      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20207      */
20208     timeout : 30000,
20209     /**
20210      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20211      * the server the name of the callback function set up by the load call to process the returned data object.
20212      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20213      * javascript output which calls this named function passing the data object as its only parameter.
20214      */
20215     callbackParam : "callback",
20216     /**
20217      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20218      * name to the request.
20219      */
20220     nocache : true,
20221
20222     /**
20223      * Load data from the configured URL, read the data object into
20224      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20225      * process that block using the passed callback.
20226      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20227      * for the request to the remote server.
20228      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20229      * object into a block of Roo.data.Records.
20230      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20231      * The function must be passed <ul>
20232      * <li>The Record block object</li>
20233      * <li>The "arg" argument from the load function</li>
20234      * <li>A boolean success indicator</li>
20235      * </ul>
20236      * @param {Object} scope The scope in which to call the callback
20237      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20238      */
20239     load : function(params, reader, callback, scope, arg){
20240         if(this.fireEvent("beforeload", this, params) !== false){
20241
20242             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20243
20244             var url = this.url;
20245             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20246             if(this.nocache){
20247                 url += "&_dc=" + (new Date().getTime());
20248             }
20249             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20250             var trans = {
20251                 id : transId,
20252                 cb : "stcCallback"+transId,
20253                 scriptId : "stcScript"+transId,
20254                 params : params,
20255                 arg : arg,
20256                 url : url,
20257                 callback : callback,
20258                 scope : scope,
20259                 reader : reader
20260             };
20261             var conn = this;
20262
20263             window[trans.cb] = function(o){
20264                 conn.handleResponse(o, trans);
20265             };
20266
20267             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20268
20269             if(this.autoAbort !== false){
20270                 this.abort();
20271             }
20272
20273             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20274
20275             var script = document.createElement("script");
20276             script.setAttribute("src", url);
20277             script.setAttribute("type", "text/javascript");
20278             script.setAttribute("id", trans.scriptId);
20279             this.head.appendChild(script);
20280
20281             this.trans = trans;
20282         }else{
20283             callback.call(scope||this, null, arg, false);
20284         }
20285     },
20286
20287     // private
20288     isLoading : function(){
20289         return this.trans ? true : false;
20290     },
20291
20292     /**
20293      * Abort the current server request.
20294      */
20295     abort : function(){
20296         if(this.isLoading()){
20297             this.destroyTrans(this.trans);
20298         }
20299     },
20300
20301     // private
20302     destroyTrans : function(trans, isLoaded){
20303         this.head.removeChild(document.getElementById(trans.scriptId));
20304         clearTimeout(trans.timeoutId);
20305         if(isLoaded){
20306             window[trans.cb] = undefined;
20307             try{
20308                 delete window[trans.cb];
20309             }catch(e){}
20310         }else{
20311             // if hasn't been loaded, wait for load to remove it to prevent script error
20312             window[trans.cb] = function(){
20313                 window[trans.cb] = undefined;
20314                 try{
20315                     delete window[trans.cb];
20316                 }catch(e){}
20317             };
20318         }
20319     },
20320
20321     // private
20322     handleResponse : function(o, trans){
20323         this.trans = false;
20324         this.destroyTrans(trans, true);
20325         var result;
20326         try {
20327             result = trans.reader.readRecords(o);
20328         }catch(e){
20329             this.fireEvent("loadexception", this, o, trans.arg, e);
20330             trans.callback.call(trans.scope||window, null, trans.arg, false);
20331             return;
20332         }
20333         this.fireEvent("load", this, o, trans.arg);
20334         trans.callback.call(trans.scope||window, result, trans.arg, true);
20335     },
20336
20337     // private
20338     handleFailure : function(trans){
20339         this.trans = false;
20340         this.destroyTrans(trans, false);
20341         this.fireEvent("loadexception", this, null, trans.arg);
20342         trans.callback.call(trans.scope||window, null, trans.arg, false);
20343     }
20344 });/*
20345  * Based on:
20346  * Ext JS Library 1.1.1
20347  * Copyright(c) 2006-2007, Ext JS, LLC.
20348  *
20349  * Originally Released Under LGPL - original licence link has changed is not relivant.
20350  *
20351  * Fork - LGPL
20352  * <script type="text/javascript">
20353  */
20354
20355 /**
20356  * @class Roo.data.JsonReader
20357  * @extends Roo.data.DataReader
20358  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20359  * based on mappings in a provided Roo.data.Record constructor.
20360  * 
20361  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20362  * in the reply previously. 
20363  * 
20364  * <p>
20365  * Example code:
20366  * <pre><code>
20367 var RecordDef = Roo.data.Record.create([
20368     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20369     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20370 ]);
20371 var myReader = new Roo.data.JsonReader({
20372     totalProperty: "results",    // The property which contains the total dataset size (optional)
20373     root: "rows",                // The property which contains an Array of row objects
20374     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20375 }, RecordDef);
20376 </code></pre>
20377  * <p>
20378  * This would consume a JSON file like this:
20379  * <pre><code>
20380 { 'results': 2, 'rows': [
20381     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20382     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20383 }
20384 </code></pre>
20385  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20386  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20387  * paged from the remote server.
20388  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20389  * @cfg {String} root name of the property which contains the Array of row objects.
20390  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20391  * @constructor
20392  * Create a new JsonReader
20393  * @param {Object} meta Metadata configuration options
20394  * @param {Object} recordType Either an Array of field definition objects,
20395  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20396  */
20397 Roo.data.JsonReader = function(meta, recordType){
20398     
20399     meta = meta || {};
20400     // set some defaults:
20401     Roo.applyIf(meta, {
20402         totalProperty: 'total',
20403         successProperty : 'success',
20404         root : 'data',
20405         id : 'id'
20406     });
20407     
20408     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20409 };
20410 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20411     
20412     /**
20413      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20414      * Used by Store query builder to append _requestMeta to params.
20415      * 
20416      */
20417     metaFromRemote : false,
20418     /**
20419      * This method is only used by a DataProxy which has retrieved data from a remote server.
20420      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20421      * @return {Object} data A data block which is used by an Roo.data.Store object as
20422      * a cache of Roo.data.Records.
20423      */
20424     read : function(response){
20425         var json = response.responseText;
20426        
20427         var o = /* eval:var:o */ eval("("+json+")");
20428         if(!o) {
20429             throw {message: "JsonReader.read: Json object not found"};
20430         }
20431         
20432         if(o.metaData){
20433             
20434             delete this.ef;
20435             this.metaFromRemote = true;
20436             this.meta = o.metaData;
20437             this.recordType = Roo.data.Record.create(o.metaData.fields);
20438             this.onMetaChange(this.meta, this.recordType, o);
20439         }
20440         return this.readRecords(o);
20441     },
20442
20443     // private function a store will implement
20444     onMetaChange : function(meta, recordType, o){
20445
20446     },
20447
20448     /**
20449          * @ignore
20450          */
20451     simpleAccess: function(obj, subsc) {
20452         return obj[subsc];
20453     },
20454
20455         /**
20456          * @ignore
20457          */
20458     getJsonAccessor: function(){
20459         var re = /[\[\.]/;
20460         return function(expr) {
20461             try {
20462                 return(re.test(expr))
20463                     ? new Function("obj", "return obj." + expr)
20464                     : function(obj){
20465                         return obj[expr];
20466                     };
20467             } catch(e){}
20468             return Roo.emptyFn;
20469         };
20470     }(),
20471
20472     /**
20473      * Create a data block containing Roo.data.Records from an XML document.
20474      * @param {Object} o An object which contains an Array of row objects in the property specified
20475      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20476      * which contains the total size of the dataset.
20477      * @return {Object} data A data block which is used by an Roo.data.Store object as
20478      * a cache of Roo.data.Records.
20479      */
20480     readRecords : function(o){
20481         /**
20482          * After any data loads, the raw JSON data is available for further custom processing.
20483          * @type Object
20484          */
20485         this.jsonData = o;
20486         var s = this.meta, Record = this.recordType,
20487             f = Record.prototype.fields, fi = f.items, fl = f.length;
20488
20489 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20490         if (!this.ef) {
20491             if(s.totalProperty) {
20492                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20493                 }
20494                 if(s.successProperty) {
20495                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20496                 }
20497                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20498                 if (s.id) {
20499                         var g = this.getJsonAccessor(s.id);
20500                         this.getId = function(rec) {
20501                                 var r = g(rec);
20502                                 return (r === undefined || r === "") ? null : r;
20503                         };
20504                 } else {
20505                         this.getId = function(){return null;};
20506                 }
20507             this.ef = [];
20508             for(var jj = 0; jj < fl; jj++){
20509                 f = fi[jj];
20510                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20511                 this.ef[jj] = this.getJsonAccessor(map);
20512             }
20513         }
20514
20515         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20516         if(s.totalProperty){
20517             var vt = parseInt(this.getTotal(o), 10);
20518             if(!isNaN(vt)){
20519                 totalRecords = vt;
20520             }
20521         }
20522         if(s.successProperty){
20523             var vs = this.getSuccess(o);
20524             if(vs === false || vs === 'false'){
20525                 success = false;
20526             }
20527         }
20528         var records = [];
20529             for(var i = 0; i < c; i++){
20530                     var n = root[i];
20531                 var values = {};
20532                 var id = this.getId(n);
20533                 for(var j = 0; j < fl; j++){
20534                     f = fi[j];
20535                 var v = this.ef[j](n);
20536                 if (!f.convert) {
20537                     Roo.log('missing convert for ' + f.name);
20538                     Roo.log(f);
20539                     continue;
20540                 }
20541                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20542                 }
20543                 var record = new Record(values, id);
20544                 record.json = n;
20545                 records[i] = record;
20546             }
20547             return {
20548                 success : success,
20549                 records : records,
20550                 totalRecords : totalRecords
20551             };
20552     }
20553 });/*
20554  * Based on:
20555  * Ext JS Library 1.1.1
20556  * Copyright(c) 2006-2007, Ext JS, LLC.
20557  *
20558  * Originally Released Under LGPL - original licence link has changed is not relivant.
20559  *
20560  * Fork - LGPL
20561  * <script type="text/javascript">
20562  */
20563
20564 /**
20565  * @class Roo.data.XmlReader
20566  * @extends Roo.data.DataReader
20567  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20568  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20569  * <p>
20570  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20571  * header in the HTTP response must be set to "text/xml".</em>
20572  * <p>
20573  * Example code:
20574  * <pre><code>
20575 var RecordDef = Roo.data.Record.create([
20576    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20577    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20578 ]);
20579 var myReader = new Roo.data.XmlReader({
20580    totalRecords: "results", // The element which contains the total dataset size (optional)
20581    record: "row",           // The repeated element which contains row information
20582    id: "id"                 // The element within the row that provides an ID for the record (optional)
20583 }, RecordDef);
20584 </code></pre>
20585  * <p>
20586  * This would consume an XML file like this:
20587  * <pre><code>
20588 &lt;?xml?>
20589 &lt;dataset>
20590  &lt;results>2&lt;/results>
20591  &lt;row>
20592    &lt;id>1&lt;/id>
20593    &lt;name>Bill&lt;/name>
20594    &lt;occupation>Gardener&lt;/occupation>
20595  &lt;/row>
20596  &lt;row>
20597    &lt;id>2&lt;/id>
20598    &lt;name>Ben&lt;/name>
20599    &lt;occupation>Horticulturalist&lt;/occupation>
20600  &lt;/row>
20601 &lt;/dataset>
20602 </code></pre>
20603  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20604  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20605  * paged from the remote server.
20606  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20607  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20608  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20609  * a record identifier value.
20610  * @constructor
20611  * Create a new XmlReader
20612  * @param {Object} meta Metadata configuration options
20613  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20614  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20615  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20616  */
20617 Roo.data.XmlReader = function(meta, recordType){
20618     meta = meta || {};
20619     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20620 };
20621 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20622     /**
20623      * This method is only used by a DataProxy which has retrieved data from a remote server.
20624          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20625          * to contain a method called 'responseXML' that returns an XML document object.
20626      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20627      * a cache of Roo.data.Records.
20628      */
20629     read : function(response){
20630         var doc = response.responseXML;
20631         if(!doc) {
20632             throw {message: "XmlReader.read: XML Document not available"};
20633         }
20634         return this.readRecords(doc);
20635     },
20636
20637     /**
20638      * Create a data block containing Roo.data.Records from an XML document.
20639          * @param {Object} doc A parsed XML document.
20640      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20641      * a cache of Roo.data.Records.
20642      */
20643     readRecords : function(doc){
20644         /**
20645          * After any data loads/reads, the raw XML Document is available for further custom processing.
20646          * @type XMLDocument
20647          */
20648         this.xmlData = doc;
20649         var root = doc.documentElement || doc;
20650         var q = Roo.DomQuery;
20651         var recordType = this.recordType, fields = recordType.prototype.fields;
20652         var sid = this.meta.id;
20653         var totalRecords = 0, success = true;
20654         if(this.meta.totalRecords){
20655             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20656         }
20657         
20658         if(this.meta.success){
20659             var sv = q.selectValue(this.meta.success, root, true);
20660             success = sv !== false && sv !== 'false';
20661         }
20662         var records = [];
20663         var ns = q.select(this.meta.record, root);
20664         for(var i = 0, len = ns.length; i < len; i++) {
20665                 var n = ns[i];
20666                 var values = {};
20667                 var id = sid ? q.selectValue(sid, n) : undefined;
20668                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20669                     var f = fields.items[j];
20670                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20671                     v = f.convert(v);
20672                     values[f.name] = v;
20673                 }
20674                 var record = new recordType(values, id);
20675                 record.node = n;
20676                 records[records.length] = record;
20677             }
20678
20679             return {
20680                 success : success,
20681                 records : records,
20682                 totalRecords : totalRecords || records.length
20683             };
20684     }
20685 });/*
20686  * Based on:
20687  * Ext JS Library 1.1.1
20688  * Copyright(c) 2006-2007, Ext JS, LLC.
20689  *
20690  * Originally Released Under LGPL - original licence link has changed is not relivant.
20691  *
20692  * Fork - LGPL
20693  * <script type="text/javascript">
20694  */
20695
20696 /**
20697  * @class Roo.data.ArrayReader
20698  * @extends Roo.data.DataReader
20699  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20700  * Each element of that Array represents a row of data fields. The
20701  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20702  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20703  * <p>
20704  * Example code:.
20705  * <pre><code>
20706 var RecordDef = Roo.data.Record.create([
20707     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20708     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20709 ]);
20710 var myReader = new Roo.data.ArrayReader({
20711     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20712 }, RecordDef);
20713 </code></pre>
20714  * <p>
20715  * This would consume an Array like this:
20716  * <pre><code>
20717 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20718   </code></pre>
20719  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20720  * @constructor
20721  * Create a new JsonReader
20722  * @param {Object} meta Metadata configuration options.
20723  * @param {Object} recordType Either an Array of field definition objects
20724  * as specified to {@link Roo.data.Record#create},
20725  * or an {@link Roo.data.Record} object
20726  * created using {@link Roo.data.Record#create}.
20727  */
20728 Roo.data.ArrayReader = function(meta, recordType){
20729     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20730 };
20731
20732 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20733     /**
20734      * Create a data block containing Roo.data.Records from an XML document.
20735      * @param {Object} o An Array of row objects which represents the dataset.
20736      * @return {Object} data A data block which is used by an Roo.data.Store object as
20737      * a cache of Roo.data.Records.
20738      */
20739     readRecords : function(o){
20740         var sid = this.meta ? this.meta.id : null;
20741         var recordType = this.recordType, fields = recordType.prototype.fields;
20742         var records = [];
20743         var root = o;
20744             for(var i = 0; i < root.length; i++){
20745                     var n = root[i];
20746                 var values = {};
20747                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20748                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20749                 var f = fields.items[j];
20750                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20751                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20752                 v = f.convert(v);
20753                 values[f.name] = v;
20754             }
20755                 var record = new recordType(values, id);
20756                 record.json = n;
20757                 records[records.length] = record;
20758             }
20759             return {
20760                 records : records,
20761                 totalRecords : records.length
20762             };
20763     }
20764 });/*
20765  * Based on:
20766  * Ext JS Library 1.1.1
20767  * Copyright(c) 2006-2007, Ext JS, LLC.
20768  *
20769  * Originally Released Under LGPL - original licence link has changed is not relivant.
20770  *
20771  * Fork - LGPL
20772  * <script type="text/javascript">
20773  */
20774
20775
20776 /**
20777  * @class Roo.data.Tree
20778  * @extends Roo.util.Observable
20779  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20780  * in the tree have most standard DOM functionality.
20781  * @constructor
20782  * @param {Node} root (optional) The root node
20783  */
20784 Roo.data.Tree = function(root){
20785    this.nodeHash = {};
20786    /**
20787     * The root node for this tree
20788     * @type Node
20789     */
20790    this.root = null;
20791    if(root){
20792        this.setRootNode(root);
20793    }
20794    this.addEvents({
20795        /**
20796         * @event append
20797         * Fires when a new child node is appended to a node in this tree.
20798         * @param {Tree} tree The owner tree
20799         * @param {Node} parent The parent node
20800         * @param {Node} node The newly appended node
20801         * @param {Number} index The index of the newly appended node
20802         */
20803        "append" : true,
20804        /**
20805         * @event remove
20806         * Fires when a child node is removed from a node in this tree.
20807         * @param {Tree} tree The owner tree
20808         * @param {Node} parent The parent node
20809         * @param {Node} node The child node removed
20810         */
20811        "remove" : true,
20812        /**
20813         * @event move
20814         * Fires when a node is moved to a new location in the tree
20815         * @param {Tree} tree The owner tree
20816         * @param {Node} node The node moved
20817         * @param {Node} oldParent The old parent of this node
20818         * @param {Node} newParent The new parent of this node
20819         * @param {Number} index The index it was moved to
20820         */
20821        "move" : true,
20822        /**
20823         * @event insert
20824         * Fires when a new child node is inserted in a node in this tree.
20825         * @param {Tree} tree The owner tree
20826         * @param {Node} parent The parent node
20827         * @param {Node} node The child node inserted
20828         * @param {Node} refNode The child node the node was inserted before
20829         */
20830        "insert" : true,
20831        /**
20832         * @event beforeappend
20833         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20834         * @param {Tree} tree The owner tree
20835         * @param {Node} parent The parent node
20836         * @param {Node} node The child node to be appended
20837         */
20838        "beforeappend" : true,
20839        /**
20840         * @event beforeremove
20841         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20842         * @param {Tree} tree The owner tree
20843         * @param {Node} parent The parent node
20844         * @param {Node} node The child node to be removed
20845         */
20846        "beforeremove" : true,
20847        /**
20848         * @event beforemove
20849         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20850         * @param {Tree} tree The owner tree
20851         * @param {Node} node The node being moved
20852         * @param {Node} oldParent The parent of the node
20853         * @param {Node} newParent The new parent the node is moving to
20854         * @param {Number} index The index it is being moved to
20855         */
20856        "beforemove" : true,
20857        /**
20858         * @event beforeinsert
20859         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20860         * @param {Tree} tree The owner tree
20861         * @param {Node} parent The parent node
20862         * @param {Node} node The child node to be inserted
20863         * @param {Node} refNode The child node the node is being inserted before
20864         */
20865        "beforeinsert" : true
20866    });
20867
20868     Roo.data.Tree.superclass.constructor.call(this);
20869 };
20870
20871 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20872     pathSeparator: "/",
20873
20874     proxyNodeEvent : function(){
20875         return this.fireEvent.apply(this, arguments);
20876     },
20877
20878     /**
20879      * Returns the root node for this tree.
20880      * @return {Node}
20881      */
20882     getRootNode : function(){
20883         return this.root;
20884     },
20885
20886     /**
20887      * Sets the root node for this tree.
20888      * @param {Node} node
20889      * @return {Node}
20890      */
20891     setRootNode : function(node){
20892         this.root = node;
20893         node.ownerTree = this;
20894         node.isRoot = true;
20895         this.registerNode(node);
20896         return node;
20897     },
20898
20899     /**
20900      * Gets a node in this tree by its id.
20901      * @param {String} id
20902      * @return {Node}
20903      */
20904     getNodeById : function(id){
20905         return this.nodeHash[id];
20906     },
20907
20908     registerNode : function(node){
20909         this.nodeHash[node.id] = node;
20910     },
20911
20912     unregisterNode : function(node){
20913         delete this.nodeHash[node.id];
20914     },
20915
20916     toString : function(){
20917         return "[Tree"+(this.id?" "+this.id:"")+"]";
20918     }
20919 });
20920
20921 /**
20922  * @class Roo.data.Node
20923  * @extends Roo.util.Observable
20924  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20925  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20926  * @constructor
20927  * @param {Object} attributes The attributes/config for the node
20928  */
20929 Roo.data.Node = function(attributes){
20930     /**
20931      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20932      * @type {Object}
20933      */
20934     this.attributes = attributes || {};
20935     this.leaf = this.attributes.leaf;
20936     /**
20937      * The node id. @type String
20938      */
20939     this.id = this.attributes.id;
20940     if(!this.id){
20941         this.id = Roo.id(null, "ynode-");
20942         this.attributes.id = this.id;
20943     }
20944     /**
20945      * All child nodes of this node. @type Array
20946      */
20947     this.childNodes = [];
20948     if(!this.childNodes.indexOf){ // indexOf is a must
20949         this.childNodes.indexOf = function(o){
20950             for(var i = 0, len = this.length; i < len; i++){
20951                 if(this[i] == o) {
20952                     return i;
20953                 }
20954             }
20955             return -1;
20956         };
20957     }
20958     /**
20959      * The parent node for this node. @type Node
20960      */
20961     this.parentNode = null;
20962     /**
20963      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20964      */
20965     this.firstChild = null;
20966     /**
20967      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20968      */
20969     this.lastChild = null;
20970     /**
20971      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20972      */
20973     this.previousSibling = null;
20974     /**
20975      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20976      */
20977     this.nextSibling = null;
20978
20979     this.addEvents({
20980        /**
20981         * @event append
20982         * Fires when a new child node is appended
20983         * @param {Tree} tree The owner tree
20984         * @param {Node} this This node
20985         * @param {Node} node The newly appended node
20986         * @param {Number} index The index of the newly appended node
20987         */
20988        "append" : true,
20989        /**
20990         * @event remove
20991         * Fires when a child node is removed
20992         * @param {Tree} tree The owner tree
20993         * @param {Node} this This node
20994         * @param {Node} node The removed node
20995         */
20996        "remove" : true,
20997        /**
20998         * @event move
20999         * Fires when this node is moved to a new location in the tree
21000         * @param {Tree} tree The owner tree
21001         * @param {Node} this This node
21002         * @param {Node} oldParent The old parent of this node
21003         * @param {Node} newParent The new parent of this node
21004         * @param {Number} index The index it was moved to
21005         */
21006        "move" : true,
21007        /**
21008         * @event insert
21009         * Fires when a new child node is inserted.
21010         * @param {Tree} tree The owner tree
21011         * @param {Node} this This node
21012         * @param {Node} node The child node inserted
21013         * @param {Node} refNode The child node the node was inserted before
21014         */
21015        "insert" : true,
21016        /**
21017         * @event beforeappend
21018         * Fires before a new child is appended, return false to cancel the append.
21019         * @param {Tree} tree The owner tree
21020         * @param {Node} this This node
21021         * @param {Node} node The child node to be appended
21022         */
21023        "beforeappend" : true,
21024        /**
21025         * @event beforeremove
21026         * Fires before a child is removed, return false to cancel the remove.
21027         * @param {Tree} tree The owner tree
21028         * @param {Node} this This node
21029         * @param {Node} node The child node to be removed
21030         */
21031        "beforeremove" : true,
21032        /**
21033         * @event beforemove
21034         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21035         * @param {Tree} tree The owner tree
21036         * @param {Node} this This node
21037         * @param {Node} oldParent The parent of this node
21038         * @param {Node} newParent The new parent this node is moving to
21039         * @param {Number} index The index it is being moved to
21040         */
21041        "beforemove" : true,
21042        /**
21043         * @event beforeinsert
21044         * Fires before a new child is inserted, return false to cancel the insert.
21045         * @param {Tree} tree The owner tree
21046         * @param {Node} this This node
21047         * @param {Node} node The child node to be inserted
21048         * @param {Node} refNode The child node the node is being inserted before
21049         */
21050        "beforeinsert" : true
21051    });
21052     this.listeners = this.attributes.listeners;
21053     Roo.data.Node.superclass.constructor.call(this);
21054 };
21055
21056 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21057     fireEvent : function(evtName){
21058         // first do standard event for this node
21059         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21060             return false;
21061         }
21062         // then bubble it up to the tree if the event wasn't cancelled
21063         var ot = this.getOwnerTree();
21064         if(ot){
21065             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21066                 return false;
21067             }
21068         }
21069         return true;
21070     },
21071
21072     /**
21073      * Returns true if this node is a leaf
21074      * @return {Boolean}
21075      */
21076     isLeaf : function(){
21077         return this.leaf === true;
21078     },
21079
21080     // private
21081     setFirstChild : function(node){
21082         this.firstChild = node;
21083     },
21084
21085     //private
21086     setLastChild : function(node){
21087         this.lastChild = node;
21088     },
21089
21090
21091     /**
21092      * Returns true if this node is the last child of its parent
21093      * @return {Boolean}
21094      */
21095     isLast : function(){
21096        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21097     },
21098
21099     /**
21100      * Returns true if this node is the first child of its parent
21101      * @return {Boolean}
21102      */
21103     isFirst : function(){
21104        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21105     },
21106
21107     hasChildNodes : function(){
21108         return !this.isLeaf() && this.childNodes.length > 0;
21109     },
21110
21111     /**
21112      * Insert node(s) as the last child node of this node.
21113      * @param {Node/Array} node The node or Array of nodes to append
21114      * @return {Node} The appended node if single append, or null if an array was passed
21115      */
21116     appendChild : function(node){
21117         var multi = false;
21118         if(node instanceof Array){
21119             multi = node;
21120         }else if(arguments.length > 1){
21121             multi = arguments;
21122         }
21123         // if passed an array or multiple args do them one by one
21124         if(multi){
21125             for(var i = 0, len = multi.length; i < len; i++) {
21126                 this.appendChild(multi[i]);
21127             }
21128         }else{
21129             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21130                 return false;
21131             }
21132             var index = this.childNodes.length;
21133             var oldParent = node.parentNode;
21134             // it's a move, make sure we move it cleanly
21135             if(oldParent){
21136                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21137                     return false;
21138                 }
21139                 oldParent.removeChild(node);
21140             }
21141             index = this.childNodes.length;
21142             if(index == 0){
21143                 this.setFirstChild(node);
21144             }
21145             this.childNodes.push(node);
21146             node.parentNode = this;
21147             var ps = this.childNodes[index-1];
21148             if(ps){
21149                 node.previousSibling = ps;
21150                 ps.nextSibling = node;
21151             }else{
21152                 node.previousSibling = null;
21153             }
21154             node.nextSibling = null;
21155             this.setLastChild(node);
21156             node.setOwnerTree(this.getOwnerTree());
21157             this.fireEvent("append", this.ownerTree, this, node, index);
21158             if(oldParent){
21159                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21160             }
21161             return node;
21162         }
21163     },
21164
21165     /**
21166      * Removes a child node from this node.
21167      * @param {Node} node The node to remove
21168      * @return {Node} The removed node
21169      */
21170     removeChild : function(node){
21171         var index = this.childNodes.indexOf(node);
21172         if(index == -1){
21173             return false;
21174         }
21175         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21176             return false;
21177         }
21178
21179         // remove it from childNodes collection
21180         this.childNodes.splice(index, 1);
21181
21182         // update siblings
21183         if(node.previousSibling){
21184             node.previousSibling.nextSibling = node.nextSibling;
21185         }
21186         if(node.nextSibling){
21187             node.nextSibling.previousSibling = node.previousSibling;
21188         }
21189
21190         // update child refs
21191         if(this.firstChild == node){
21192             this.setFirstChild(node.nextSibling);
21193         }
21194         if(this.lastChild == node){
21195             this.setLastChild(node.previousSibling);
21196         }
21197
21198         node.setOwnerTree(null);
21199         // clear any references from the node
21200         node.parentNode = null;
21201         node.previousSibling = null;
21202         node.nextSibling = null;
21203         this.fireEvent("remove", this.ownerTree, this, node);
21204         return node;
21205     },
21206
21207     /**
21208      * Inserts the first node before the second node in this nodes childNodes collection.
21209      * @param {Node} node The node to insert
21210      * @param {Node} refNode The node to insert before (if null the node is appended)
21211      * @return {Node} The inserted node
21212      */
21213     insertBefore : function(node, refNode){
21214         if(!refNode){ // like standard Dom, refNode can be null for append
21215             return this.appendChild(node);
21216         }
21217         // nothing to do
21218         if(node == refNode){
21219             return false;
21220         }
21221
21222         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21223             return false;
21224         }
21225         var index = this.childNodes.indexOf(refNode);
21226         var oldParent = node.parentNode;
21227         var refIndex = index;
21228
21229         // when moving internally, indexes will change after remove
21230         if(oldParent == this && this.childNodes.indexOf(node) < index){
21231             refIndex--;
21232         }
21233
21234         // it's a move, make sure we move it cleanly
21235         if(oldParent){
21236             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21237                 return false;
21238             }
21239             oldParent.removeChild(node);
21240         }
21241         if(refIndex == 0){
21242             this.setFirstChild(node);
21243         }
21244         this.childNodes.splice(refIndex, 0, node);
21245         node.parentNode = this;
21246         var ps = this.childNodes[refIndex-1];
21247         if(ps){
21248             node.previousSibling = ps;
21249             ps.nextSibling = node;
21250         }else{
21251             node.previousSibling = null;
21252         }
21253         node.nextSibling = refNode;
21254         refNode.previousSibling = node;
21255         node.setOwnerTree(this.getOwnerTree());
21256         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21257         if(oldParent){
21258             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21259         }
21260         return node;
21261     },
21262
21263     /**
21264      * Returns the child node at the specified index.
21265      * @param {Number} index
21266      * @return {Node}
21267      */
21268     item : function(index){
21269         return this.childNodes[index];
21270     },
21271
21272     /**
21273      * Replaces one child node in this node with another.
21274      * @param {Node} newChild The replacement node
21275      * @param {Node} oldChild The node to replace
21276      * @return {Node} The replaced node
21277      */
21278     replaceChild : function(newChild, oldChild){
21279         this.insertBefore(newChild, oldChild);
21280         this.removeChild(oldChild);
21281         return oldChild;
21282     },
21283
21284     /**
21285      * Returns the index of a child node
21286      * @param {Node} node
21287      * @return {Number} The index of the node or -1 if it was not found
21288      */
21289     indexOf : function(child){
21290         return this.childNodes.indexOf(child);
21291     },
21292
21293     /**
21294      * Returns the tree this node is in.
21295      * @return {Tree}
21296      */
21297     getOwnerTree : function(){
21298         // if it doesn't have one, look for one
21299         if(!this.ownerTree){
21300             var p = this;
21301             while(p){
21302                 if(p.ownerTree){
21303                     this.ownerTree = p.ownerTree;
21304                     break;
21305                 }
21306                 p = p.parentNode;
21307             }
21308         }
21309         return this.ownerTree;
21310     },
21311
21312     /**
21313      * Returns depth of this node (the root node has a depth of 0)
21314      * @return {Number}
21315      */
21316     getDepth : function(){
21317         var depth = 0;
21318         var p = this;
21319         while(p.parentNode){
21320             ++depth;
21321             p = p.parentNode;
21322         }
21323         return depth;
21324     },
21325
21326     // private
21327     setOwnerTree : function(tree){
21328         // if it's move, we need to update everyone
21329         if(tree != this.ownerTree){
21330             if(this.ownerTree){
21331                 this.ownerTree.unregisterNode(this);
21332             }
21333             this.ownerTree = tree;
21334             var cs = this.childNodes;
21335             for(var i = 0, len = cs.length; i < len; i++) {
21336                 cs[i].setOwnerTree(tree);
21337             }
21338             if(tree){
21339                 tree.registerNode(this);
21340             }
21341         }
21342     },
21343
21344     /**
21345      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21346      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21347      * @return {String} The path
21348      */
21349     getPath : function(attr){
21350         attr = attr || "id";
21351         var p = this.parentNode;
21352         var b = [this.attributes[attr]];
21353         while(p){
21354             b.unshift(p.attributes[attr]);
21355             p = p.parentNode;
21356         }
21357         var sep = this.getOwnerTree().pathSeparator;
21358         return sep + b.join(sep);
21359     },
21360
21361     /**
21362      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21363      * function call will be the scope provided or the current node. The arguments to the function
21364      * will be the args provided or the current node. If the function returns false at any point,
21365      * the bubble is stopped.
21366      * @param {Function} fn The function to call
21367      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21368      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21369      */
21370     bubble : function(fn, scope, args){
21371         var p = this;
21372         while(p){
21373             if(fn.call(scope || p, args || p) === false){
21374                 break;
21375             }
21376             p = p.parentNode;
21377         }
21378     },
21379
21380     /**
21381      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21382      * function call will be the scope provided or the current node. The arguments to the function
21383      * will be the args provided or the current node. If the function returns false at any point,
21384      * the cascade is stopped on that branch.
21385      * @param {Function} fn The function to call
21386      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21387      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21388      */
21389     cascade : function(fn, scope, args){
21390         if(fn.call(scope || this, args || this) !== false){
21391             var cs = this.childNodes;
21392             for(var i = 0, len = cs.length; i < len; i++) {
21393                 cs[i].cascade(fn, scope, args);
21394             }
21395         }
21396     },
21397
21398     /**
21399      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21400      * function call will be the scope provided or the current node. The arguments to the function
21401      * will be the args provided or the current node. If the function returns false at any point,
21402      * the iteration stops.
21403      * @param {Function} fn The function to call
21404      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21405      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21406      */
21407     eachChild : function(fn, scope, args){
21408         var cs = this.childNodes;
21409         for(var i = 0, len = cs.length; i < len; i++) {
21410                 if(fn.call(scope || this, args || cs[i]) === false){
21411                     break;
21412                 }
21413         }
21414     },
21415
21416     /**
21417      * Finds the first child that has the attribute with the specified value.
21418      * @param {String} attribute The attribute name
21419      * @param {Mixed} value The value to search for
21420      * @return {Node} The found child or null if none was found
21421      */
21422     findChild : function(attribute, value){
21423         var cs = this.childNodes;
21424         for(var i = 0, len = cs.length; i < len; i++) {
21425                 if(cs[i].attributes[attribute] == value){
21426                     return cs[i];
21427                 }
21428         }
21429         return null;
21430     },
21431
21432     /**
21433      * Finds the first child by a custom function. The child matches if the function passed
21434      * returns true.
21435      * @param {Function} fn
21436      * @param {Object} scope (optional)
21437      * @return {Node} The found child or null if none was found
21438      */
21439     findChildBy : function(fn, scope){
21440         var cs = this.childNodes;
21441         for(var i = 0, len = cs.length; i < len; i++) {
21442                 if(fn.call(scope||cs[i], cs[i]) === true){
21443                     return cs[i];
21444                 }
21445         }
21446         return null;
21447     },
21448
21449     /**
21450      * Sorts this nodes children using the supplied sort function
21451      * @param {Function} fn
21452      * @param {Object} scope (optional)
21453      */
21454     sort : function(fn, scope){
21455         var cs = this.childNodes;
21456         var len = cs.length;
21457         if(len > 0){
21458             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21459             cs.sort(sortFn);
21460             for(var i = 0; i < len; i++){
21461                 var n = cs[i];
21462                 n.previousSibling = cs[i-1];
21463                 n.nextSibling = cs[i+1];
21464                 if(i == 0){
21465                     this.setFirstChild(n);
21466                 }
21467                 if(i == len-1){
21468                     this.setLastChild(n);
21469                 }
21470             }
21471         }
21472     },
21473
21474     /**
21475      * Returns true if this node is an ancestor (at any point) of the passed node.
21476      * @param {Node} node
21477      * @return {Boolean}
21478      */
21479     contains : function(node){
21480         return node.isAncestor(this);
21481     },
21482
21483     /**
21484      * Returns true if the passed node is an ancestor (at any point) of this node.
21485      * @param {Node} node
21486      * @return {Boolean}
21487      */
21488     isAncestor : function(node){
21489         var p = this.parentNode;
21490         while(p){
21491             if(p == node){
21492                 return true;
21493             }
21494             p = p.parentNode;
21495         }
21496         return false;
21497     },
21498
21499     toString : function(){
21500         return "[Node"+(this.id?" "+this.id:"")+"]";
21501     }
21502 });/*
21503  * Based on:
21504  * Ext JS Library 1.1.1
21505  * Copyright(c) 2006-2007, Ext JS, LLC.
21506  *
21507  * Originally Released Under LGPL - original licence link has changed is not relivant.
21508  *
21509  * Fork - LGPL
21510  * <script type="text/javascript">
21511  */
21512  
21513
21514 /**
21515  * @class Roo.ComponentMgr
21516  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21517  * @singleton
21518  */
21519 Roo.ComponentMgr = function(){
21520     var all = new Roo.util.MixedCollection();
21521
21522     return {
21523         /**
21524          * Registers a component.
21525          * @param {Roo.Component} c The component
21526          */
21527         register : function(c){
21528             all.add(c);
21529         },
21530
21531         /**
21532          * Unregisters a component.
21533          * @param {Roo.Component} c The component
21534          */
21535         unregister : function(c){
21536             all.remove(c);
21537         },
21538
21539         /**
21540          * Returns a component by id
21541          * @param {String} id The component id
21542          */
21543         get : function(id){
21544             return all.get(id);
21545         },
21546
21547         /**
21548          * Registers a function that will be called when a specified component is added to ComponentMgr
21549          * @param {String} id The component id
21550          * @param {Funtction} fn The callback function
21551          * @param {Object} scope The scope of the callback
21552          */
21553         onAvailable : function(id, fn, scope){
21554             all.on("add", function(index, o){
21555                 if(o.id == id){
21556                     fn.call(scope || o, o);
21557                     all.un("add", fn, scope);
21558                 }
21559             });
21560         }
21561     };
21562 }();/*
21563  * Based on:
21564  * Ext JS Library 1.1.1
21565  * Copyright(c) 2006-2007, Ext JS, LLC.
21566  *
21567  * Originally Released Under LGPL - original licence link has changed is not relivant.
21568  *
21569  * Fork - LGPL
21570  * <script type="text/javascript">
21571  */
21572  
21573 /**
21574  * @class Roo.Component
21575  * @extends Roo.util.Observable
21576  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21577  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21578  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21579  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21580  * All visual components (widgets) that require rendering into a layout should subclass Component.
21581  * @constructor
21582  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21583  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21584  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21585  */
21586 Roo.Component = function(config){
21587     config = config || {};
21588     if(config.tagName || config.dom || typeof config == "string"){ // element object
21589         config = {el: config, id: config.id || config};
21590     }
21591     this.initialConfig = config;
21592
21593     Roo.apply(this, config);
21594     this.addEvents({
21595         /**
21596          * @event disable
21597          * Fires after the component is disabled.
21598              * @param {Roo.Component} this
21599              */
21600         disable : true,
21601         /**
21602          * @event enable
21603          * Fires after the component is enabled.
21604              * @param {Roo.Component} this
21605              */
21606         enable : true,
21607         /**
21608          * @event beforeshow
21609          * Fires before the component is shown.  Return false to stop the show.
21610              * @param {Roo.Component} this
21611              */
21612         beforeshow : true,
21613         /**
21614          * @event show
21615          * Fires after the component is shown.
21616              * @param {Roo.Component} this
21617              */
21618         show : true,
21619         /**
21620          * @event beforehide
21621          * Fires before the component is hidden. Return false to stop the hide.
21622              * @param {Roo.Component} this
21623              */
21624         beforehide : true,
21625         /**
21626          * @event hide
21627          * Fires after the component is hidden.
21628              * @param {Roo.Component} this
21629              */
21630         hide : true,
21631         /**
21632          * @event beforerender
21633          * Fires before the component is rendered. Return false to stop the render.
21634              * @param {Roo.Component} this
21635              */
21636         beforerender : true,
21637         /**
21638          * @event render
21639          * Fires after the component is rendered.
21640              * @param {Roo.Component} this
21641              */
21642         render : true,
21643         /**
21644          * @event beforedestroy
21645          * Fires before the component is destroyed. Return false to stop the destroy.
21646              * @param {Roo.Component} this
21647              */
21648         beforedestroy : true,
21649         /**
21650          * @event destroy
21651          * Fires after the component is destroyed.
21652              * @param {Roo.Component} this
21653              */
21654         destroy : true
21655     });
21656     if(!this.id){
21657         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21658     }
21659     Roo.ComponentMgr.register(this);
21660     Roo.Component.superclass.constructor.call(this);
21661     this.initComponent();
21662     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21663         this.render(this.renderTo);
21664         delete this.renderTo;
21665     }
21666 };
21667
21668 // private
21669 Roo.Component.AUTO_ID = 1000;
21670
21671 Roo.extend(Roo.Component, Roo.util.Observable, {
21672     /**
21673      * @property {Boolean} hidden
21674      * true if this component is hidden. Read-only.
21675      */
21676     hidden : false,
21677     /**
21678      * true if this component is disabled. Read-only.
21679      */
21680     disabled : false,
21681     /**
21682      * true if this component has been rendered. Read-only.
21683      */
21684     rendered : false,
21685     
21686     /** @cfg {String} disableClass
21687      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21688      */
21689     disabledClass : "x-item-disabled",
21690         /** @cfg {Boolean} allowDomMove
21691          * Whether the component can move the Dom node when rendering (defaults to true).
21692          */
21693     allowDomMove : true,
21694     /** @cfg {String} hideMode
21695      * How this component should hidden. Supported values are
21696      * "visibility" (css visibility), "offsets" (negative offset position) and
21697      * "display" (css display) - defaults to "display".
21698      */
21699     hideMode: 'display',
21700
21701     // private
21702     ctype : "Roo.Component",
21703
21704     /** @cfg {String} actionMode 
21705      * which property holds the element that used for  hide() / show() / disable() / enable()
21706      * default is 'el' 
21707      */
21708     actionMode : "el",
21709
21710     // private
21711     getActionEl : function(){
21712         return this[this.actionMode];
21713     },
21714
21715     initComponent : Roo.emptyFn,
21716     /**
21717      * If this is a lazy rendering component, render it to its container element.
21718      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21719      */
21720     render : function(container, position){
21721         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21722             if(!container && this.el){
21723                 this.el = Roo.get(this.el);
21724                 container = this.el.dom.parentNode;
21725                 this.allowDomMove = false;
21726             }
21727             this.container = Roo.get(container);
21728             this.rendered = true;
21729             if(position !== undefined){
21730                 if(typeof position == 'number'){
21731                     position = this.container.dom.childNodes[position];
21732                 }else{
21733                     position = Roo.getDom(position);
21734                 }
21735             }
21736             this.onRender(this.container, position || null);
21737             if(this.cls){
21738                 this.el.addClass(this.cls);
21739                 delete this.cls;
21740             }
21741             if(this.style){
21742                 this.el.applyStyles(this.style);
21743                 delete this.style;
21744             }
21745             this.fireEvent("render", this);
21746             this.afterRender(this.container);
21747             if(this.hidden){
21748                 this.hide();
21749             }
21750             if(this.disabled){
21751                 this.disable();
21752             }
21753         }
21754         return this;
21755     },
21756
21757     // private
21758     // default function is not really useful
21759     onRender : function(ct, position){
21760         if(this.el){
21761             this.el = Roo.get(this.el);
21762             if(this.allowDomMove !== false){
21763                 ct.dom.insertBefore(this.el.dom, position);
21764             }
21765         }
21766     },
21767
21768     // private
21769     getAutoCreate : function(){
21770         var cfg = typeof this.autoCreate == "object" ?
21771                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21772         if(this.id && !cfg.id){
21773             cfg.id = this.id;
21774         }
21775         return cfg;
21776     },
21777
21778     // private
21779     afterRender : Roo.emptyFn,
21780
21781     /**
21782      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21783      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21784      */
21785     destroy : function(){
21786         if(this.fireEvent("beforedestroy", this) !== false){
21787             this.purgeListeners();
21788             this.beforeDestroy();
21789             if(this.rendered){
21790                 this.el.removeAllListeners();
21791                 this.el.remove();
21792                 if(this.actionMode == "container"){
21793                     this.container.remove();
21794                 }
21795             }
21796             this.onDestroy();
21797             Roo.ComponentMgr.unregister(this);
21798             this.fireEvent("destroy", this);
21799         }
21800     },
21801
21802         // private
21803     beforeDestroy : function(){
21804
21805     },
21806
21807         // private
21808         onDestroy : function(){
21809
21810     },
21811
21812     /**
21813      * Returns the underlying {@link Roo.Element}.
21814      * @return {Roo.Element} The element
21815      */
21816     getEl : function(){
21817         return this.el;
21818     },
21819
21820     /**
21821      * Returns the id of this component.
21822      * @return {String}
21823      */
21824     getId : function(){
21825         return this.id;
21826     },
21827
21828     /**
21829      * Try to focus this component.
21830      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21831      * @return {Roo.Component} this
21832      */
21833     focus : function(selectText){
21834         if(this.rendered){
21835             this.el.focus();
21836             if(selectText === true){
21837                 this.el.dom.select();
21838             }
21839         }
21840         return this;
21841     },
21842
21843     // private
21844     blur : function(){
21845         if(this.rendered){
21846             this.el.blur();
21847         }
21848         return this;
21849     },
21850
21851     /**
21852      * Disable this component.
21853      * @return {Roo.Component} this
21854      */
21855     disable : function(){
21856         if(this.rendered){
21857             this.onDisable();
21858         }
21859         this.disabled = true;
21860         this.fireEvent("disable", this);
21861         return this;
21862     },
21863
21864         // private
21865     onDisable : function(){
21866         this.getActionEl().addClass(this.disabledClass);
21867         this.el.dom.disabled = true;
21868     },
21869
21870     /**
21871      * Enable this component.
21872      * @return {Roo.Component} this
21873      */
21874     enable : function(){
21875         if(this.rendered){
21876             this.onEnable();
21877         }
21878         this.disabled = false;
21879         this.fireEvent("enable", this);
21880         return this;
21881     },
21882
21883         // private
21884     onEnable : function(){
21885         this.getActionEl().removeClass(this.disabledClass);
21886         this.el.dom.disabled = false;
21887     },
21888
21889     /**
21890      * Convenience function for setting disabled/enabled by boolean.
21891      * @param {Boolean} disabled
21892      */
21893     setDisabled : function(disabled){
21894         this[disabled ? "disable" : "enable"]();
21895     },
21896
21897     /**
21898      * Show this component.
21899      * @return {Roo.Component} this
21900      */
21901     show: function(){
21902         if(this.fireEvent("beforeshow", this) !== false){
21903             this.hidden = false;
21904             if(this.rendered){
21905                 this.onShow();
21906             }
21907             this.fireEvent("show", this);
21908         }
21909         return this;
21910     },
21911
21912     // private
21913     onShow : function(){
21914         var ae = this.getActionEl();
21915         if(this.hideMode == 'visibility'){
21916             ae.dom.style.visibility = "visible";
21917         }else if(this.hideMode == 'offsets'){
21918             ae.removeClass('x-hidden');
21919         }else{
21920             ae.dom.style.display = "";
21921         }
21922     },
21923
21924     /**
21925      * Hide this component.
21926      * @return {Roo.Component} this
21927      */
21928     hide: function(){
21929         if(this.fireEvent("beforehide", this) !== false){
21930             this.hidden = true;
21931             if(this.rendered){
21932                 this.onHide();
21933             }
21934             this.fireEvent("hide", this);
21935         }
21936         return this;
21937     },
21938
21939     // private
21940     onHide : function(){
21941         var ae = this.getActionEl();
21942         if(this.hideMode == 'visibility'){
21943             ae.dom.style.visibility = "hidden";
21944         }else if(this.hideMode == 'offsets'){
21945             ae.addClass('x-hidden');
21946         }else{
21947             ae.dom.style.display = "none";
21948         }
21949     },
21950
21951     /**
21952      * Convenience function to hide or show this component by boolean.
21953      * @param {Boolean} visible True to show, false to hide
21954      * @return {Roo.Component} this
21955      */
21956     setVisible: function(visible){
21957         if(visible) {
21958             this.show();
21959         }else{
21960             this.hide();
21961         }
21962         return this;
21963     },
21964
21965     /**
21966      * Returns true if this component is visible.
21967      */
21968     isVisible : function(){
21969         return this.getActionEl().isVisible();
21970     },
21971
21972     cloneConfig : function(overrides){
21973         overrides = overrides || {};
21974         var id = overrides.id || Roo.id();
21975         var cfg = Roo.applyIf(overrides, this.initialConfig);
21976         cfg.id = id; // prevent dup id
21977         return new this.constructor(cfg);
21978     }
21979 });/*
21980  * Based on:
21981  * Ext JS Library 1.1.1
21982  * Copyright(c) 2006-2007, Ext JS, LLC.
21983  *
21984  * Originally Released Under LGPL - original licence link has changed is not relivant.
21985  *
21986  * Fork - LGPL
21987  * <script type="text/javascript">
21988  */
21989  (function(){ 
21990 /**
21991  * @class Roo.Layer
21992  * @extends Roo.Element
21993  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21994  * automatic maintaining of shadow/shim positions.
21995  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21996  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21997  * you can pass a string with a CSS class name. False turns off the shadow.
21998  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21999  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22000  * @cfg {String} cls CSS class to add to the element
22001  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22002  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22003  * @constructor
22004  * @param {Object} config An object with config options.
22005  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22006  */
22007
22008 Roo.Layer = function(config, existingEl){
22009     config = config || {};
22010     var dh = Roo.DomHelper;
22011     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22012     if(existingEl){
22013         this.dom = Roo.getDom(existingEl);
22014     }
22015     if(!this.dom){
22016         var o = config.dh || {tag: "div", cls: "x-layer"};
22017         this.dom = dh.append(pel, o);
22018     }
22019     if(config.cls){
22020         this.addClass(config.cls);
22021     }
22022     this.constrain = config.constrain !== false;
22023     this.visibilityMode = Roo.Element.VISIBILITY;
22024     if(config.id){
22025         this.id = this.dom.id = config.id;
22026     }else{
22027         this.id = Roo.id(this.dom);
22028     }
22029     this.zindex = config.zindex || this.getZIndex();
22030     this.position("absolute", this.zindex);
22031     if(config.shadow){
22032         this.shadowOffset = config.shadowOffset || 4;
22033         this.shadow = new Roo.Shadow({
22034             offset : this.shadowOffset,
22035             mode : config.shadow
22036         });
22037     }else{
22038         this.shadowOffset = 0;
22039     }
22040     this.useShim = config.shim !== false && Roo.useShims;
22041     this.useDisplay = config.useDisplay;
22042     this.hide();
22043 };
22044
22045 var supr = Roo.Element.prototype;
22046
22047 // shims are shared among layer to keep from having 100 iframes
22048 var shims = [];
22049
22050 Roo.extend(Roo.Layer, Roo.Element, {
22051
22052     getZIndex : function(){
22053         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22054     },
22055
22056     getShim : function(){
22057         if(!this.useShim){
22058             return null;
22059         }
22060         if(this.shim){
22061             return this.shim;
22062         }
22063         var shim = shims.shift();
22064         if(!shim){
22065             shim = this.createShim();
22066             shim.enableDisplayMode('block');
22067             shim.dom.style.display = 'none';
22068             shim.dom.style.visibility = 'visible';
22069         }
22070         var pn = this.dom.parentNode;
22071         if(shim.dom.parentNode != pn){
22072             pn.insertBefore(shim.dom, this.dom);
22073         }
22074         shim.setStyle('z-index', this.getZIndex()-2);
22075         this.shim = shim;
22076         return shim;
22077     },
22078
22079     hideShim : function(){
22080         if(this.shim){
22081             this.shim.setDisplayed(false);
22082             shims.push(this.shim);
22083             delete this.shim;
22084         }
22085     },
22086
22087     disableShadow : function(){
22088         if(this.shadow){
22089             this.shadowDisabled = true;
22090             this.shadow.hide();
22091             this.lastShadowOffset = this.shadowOffset;
22092             this.shadowOffset = 0;
22093         }
22094     },
22095
22096     enableShadow : function(show){
22097         if(this.shadow){
22098             this.shadowDisabled = false;
22099             this.shadowOffset = this.lastShadowOffset;
22100             delete this.lastShadowOffset;
22101             if(show){
22102                 this.sync(true);
22103             }
22104         }
22105     },
22106
22107     // private
22108     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22109     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22110     sync : function(doShow){
22111         var sw = this.shadow;
22112         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22113             var sh = this.getShim();
22114
22115             var w = this.getWidth(),
22116                 h = this.getHeight();
22117
22118             var l = this.getLeft(true),
22119                 t = this.getTop(true);
22120
22121             if(sw && !this.shadowDisabled){
22122                 if(doShow && !sw.isVisible()){
22123                     sw.show(this);
22124                 }else{
22125                     sw.realign(l, t, w, h);
22126                 }
22127                 if(sh){
22128                     if(doShow){
22129                        sh.show();
22130                     }
22131                     // fit the shim behind the shadow, so it is shimmed too
22132                     var a = sw.adjusts, s = sh.dom.style;
22133                     s.left = (Math.min(l, l+a.l))+"px";
22134                     s.top = (Math.min(t, t+a.t))+"px";
22135                     s.width = (w+a.w)+"px";
22136                     s.height = (h+a.h)+"px";
22137                 }
22138             }else if(sh){
22139                 if(doShow){
22140                    sh.show();
22141                 }
22142                 sh.setSize(w, h);
22143                 sh.setLeftTop(l, t);
22144             }
22145             
22146         }
22147     },
22148
22149     // private
22150     destroy : function(){
22151         this.hideShim();
22152         if(this.shadow){
22153             this.shadow.hide();
22154         }
22155         this.removeAllListeners();
22156         var pn = this.dom.parentNode;
22157         if(pn){
22158             pn.removeChild(this.dom);
22159         }
22160         Roo.Element.uncache(this.id);
22161     },
22162
22163     remove : function(){
22164         this.destroy();
22165     },
22166
22167     // private
22168     beginUpdate : function(){
22169         this.updating = true;
22170     },
22171
22172     // private
22173     endUpdate : function(){
22174         this.updating = false;
22175         this.sync(true);
22176     },
22177
22178     // private
22179     hideUnders : function(negOffset){
22180         if(this.shadow){
22181             this.shadow.hide();
22182         }
22183         this.hideShim();
22184     },
22185
22186     // private
22187     constrainXY : function(){
22188         if(this.constrain){
22189             var vw = Roo.lib.Dom.getViewWidth(),
22190                 vh = Roo.lib.Dom.getViewHeight();
22191             var s = Roo.get(document).getScroll();
22192
22193             var xy = this.getXY();
22194             var x = xy[0], y = xy[1];   
22195             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22196             // only move it if it needs it
22197             var moved = false;
22198             // first validate right/bottom
22199             if((x + w) > vw+s.left){
22200                 x = vw - w - this.shadowOffset;
22201                 moved = true;
22202             }
22203             if((y + h) > vh+s.top){
22204                 y = vh - h - this.shadowOffset;
22205                 moved = true;
22206             }
22207             // then make sure top/left isn't negative
22208             if(x < s.left){
22209                 x = s.left;
22210                 moved = true;
22211             }
22212             if(y < s.top){
22213                 y = s.top;
22214                 moved = true;
22215             }
22216             if(moved){
22217                 if(this.avoidY){
22218                     var ay = this.avoidY;
22219                     if(y <= ay && (y+h) >= ay){
22220                         y = ay-h-5;   
22221                     }
22222                 }
22223                 xy = [x, y];
22224                 this.storeXY(xy);
22225                 supr.setXY.call(this, xy);
22226                 this.sync();
22227             }
22228         }
22229     },
22230
22231     isVisible : function(){
22232         return this.visible;    
22233     },
22234
22235     // private
22236     showAction : function(){
22237         this.visible = true; // track visibility to prevent getStyle calls
22238         if(this.useDisplay === true){
22239             this.setDisplayed("");
22240         }else if(this.lastXY){
22241             supr.setXY.call(this, this.lastXY);
22242         }else if(this.lastLT){
22243             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22244         }
22245     },
22246
22247     // private
22248     hideAction : function(){
22249         this.visible = false;
22250         if(this.useDisplay === true){
22251             this.setDisplayed(false);
22252         }else{
22253             this.setLeftTop(-10000,-10000);
22254         }
22255     },
22256
22257     // overridden Element method
22258     setVisible : function(v, a, d, c, e){
22259         if(v){
22260             this.showAction();
22261         }
22262         if(a && v){
22263             var cb = function(){
22264                 this.sync(true);
22265                 if(c){
22266                     c();
22267                 }
22268             }.createDelegate(this);
22269             supr.setVisible.call(this, true, true, d, cb, e);
22270         }else{
22271             if(!v){
22272                 this.hideUnders(true);
22273             }
22274             var cb = c;
22275             if(a){
22276                 cb = function(){
22277                     this.hideAction();
22278                     if(c){
22279                         c();
22280                     }
22281                 }.createDelegate(this);
22282             }
22283             supr.setVisible.call(this, v, a, d, cb, e);
22284             if(v){
22285                 this.sync(true);
22286             }else if(!a){
22287                 this.hideAction();
22288             }
22289         }
22290     },
22291
22292     storeXY : function(xy){
22293         delete this.lastLT;
22294         this.lastXY = xy;
22295     },
22296
22297     storeLeftTop : function(left, top){
22298         delete this.lastXY;
22299         this.lastLT = [left, top];
22300     },
22301
22302     // private
22303     beforeFx : function(){
22304         this.beforeAction();
22305         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22306     },
22307
22308     // private
22309     afterFx : function(){
22310         Roo.Layer.superclass.afterFx.apply(this, arguments);
22311         this.sync(this.isVisible());
22312     },
22313
22314     // private
22315     beforeAction : function(){
22316         if(!this.updating && this.shadow){
22317             this.shadow.hide();
22318         }
22319     },
22320
22321     // overridden Element method
22322     setLeft : function(left){
22323         this.storeLeftTop(left, this.getTop(true));
22324         supr.setLeft.apply(this, arguments);
22325         this.sync();
22326     },
22327
22328     setTop : function(top){
22329         this.storeLeftTop(this.getLeft(true), top);
22330         supr.setTop.apply(this, arguments);
22331         this.sync();
22332     },
22333
22334     setLeftTop : function(left, top){
22335         this.storeLeftTop(left, top);
22336         supr.setLeftTop.apply(this, arguments);
22337         this.sync();
22338     },
22339
22340     setXY : function(xy, a, d, c, e){
22341         this.fixDisplay();
22342         this.beforeAction();
22343         this.storeXY(xy);
22344         var cb = this.createCB(c);
22345         supr.setXY.call(this, xy, a, d, cb, e);
22346         if(!a){
22347             cb();
22348         }
22349     },
22350
22351     // private
22352     createCB : function(c){
22353         var el = this;
22354         return function(){
22355             el.constrainXY();
22356             el.sync(true);
22357             if(c){
22358                 c();
22359             }
22360         };
22361     },
22362
22363     // overridden Element method
22364     setX : function(x, a, d, c, e){
22365         this.setXY([x, this.getY()], a, d, c, e);
22366     },
22367
22368     // overridden Element method
22369     setY : function(y, a, d, c, e){
22370         this.setXY([this.getX(), y], a, d, c, e);
22371     },
22372
22373     // overridden Element method
22374     setSize : function(w, h, a, d, c, e){
22375         this.beforeAction();
22376         var cb = this.createCB(c);
22377         supr.setSize.call(this, w, h, a, d, cb, e);
22378         if(!a){
22379             cb();
22380         }
22381     },
22382
22383     // overridden Element method
22384     setWidth : function(w, a, d, c, e){
22385         this.beforeAction();
22386         var cb = this.createCB(c);
22387         supr.setWidth.call(this, w, a, d, cb, e);
22388         if(!a){
22389             cb();
22390         }
22391     },
22392
22393     // overridden Element method
22394     setHeight : function(h, a, d, c, e){
22395         this.beforeAction();
22396         var cb = this.createCB(c);
22397         supr.setHeight.call(this, h, a, d, cb, e);
22398         if(!a){
22399             cb();
22400         }
22401     },
22402
22403     // overridden Element method
22404     setBounds : function(x, y, w, h, a, d, c, e){
22405         this.beforeAction();
22406         var cb = this.createCB(c);
22407         if(!a){
22408             this.storeXY([x, y]);
22409             supr.setXY.call(this, [x, y]);
22410             supr.setSize.call(this, w, h, a, d, cb, e);
22411             cb();
22412         }else{
22413             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22414         }
22415         return this;
22416     },
22417     
22418     /**
22419      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22420      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22421      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22422      * @param {Number} zindex The new z-index to set
22423      * @return {this} The Layer
22424      */
22425     setZIndex : function(zindex){
22426         this.zindex = zindex;
22427         this.setStyle("z-index", zindex + 2);
22428         if(this.shadow){
22429             this.shadow.setZIndex(zindex + 1);
22430         }
22431         if(this.shim){
22432             this.shim.setStyle("z-index", zindex);
22433         }
22434     }
22435 });
22436 })();/*
22437  * Based on:
22438  * Ext JS Library 1.1.1
22439  * Copyright(c) 2006-2007, Ext JS, LLC.
22440  *
22441  * Originally Released Under LGPL - original licence link has changed is not relivant.
22442  *
22443  * Fork - LGPL
22444  * <script type="text/javascript">
22445  */
22446
22447
22448 /**
22449  * @class Roo.Shadow
22450  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22451  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22452  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22453  * @constructor
22454  * Create a new Shadow
22455  * @param {Object} config The config object
22456  */
22457 Roo.Shadow = function(config){
22458     Roo.apply(this, config);
22459     if(typeof this.mode != "string"){
22460         this.mode = this.defaultMode;
22461     }
22462     var o = this.offset, a = {h: 0};
22463     var rad = Math.floor(this.offset/2);
22464     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22465         case "drop":
22466             a.w = 0;
22467             a.l = a.t = o;
22468             a.t -= 1;
22469             if(Roo.isIE){
22470                 a.l -= this.offset + rad;
22471                 a.t -= this.offset + rad;
22472                 a.w -= rad;
22473                 a.h -= rad;
22474                 a.t += 1;
22475             }
22476         break;
22477         case "sides":
22478             a.w = (o*2);
22479             a.l = -o;
22480             a.t = o-1;
22481             if(Roo.isIE){
22482                 a.l -= (this.offset - rad);
22483                 a.t -= this.offset + rad;
22484                 a.l += 1;
22485                 a.w -= (this.offset - rad)*2;
22486                 a.w -= rad + 1;
22487                 a.h -= 1;
22488             }
22489         break;
22490         case "frame":
22491             a.w = a.h = (o*2);
22492             a.l = a.t = -o;
22493             a.t += 1;
22494             a.h -= 2;
22495             if(Roo.isIE){
22496                 a.l -= (this.offset - rad);
22497                 a.t -= (this.offset - rad);
22498                 a.l += 1;
22499                 a.w -= (this.offset + rad + 1);
22500                 a.h -= (this.offset + rad);
22501                 a.h += 1;
22502             }
22503         break;
22504     };
22505
22506     this.adjusts = a;
22507 };
22508
22509 Roo.Shadow.prototype = {
22510     /**
22511      * @cfg {String} mode
22512      * The shadow display mode.  Supports the following options:<br />
22513      * sides: Shadow displays on both sides and bottom only<br />
22514      * frame: Shadow displays equally on all four sides<br />
22515      * drop: Traditional bottom-right drop shadow (default)
22516      */
22517     /**
22518      * @cfg {String} offset
22519      * The number of pixels to offset the shadow from the element (defaults to 4)
22520      */
22521     offset: 4,
22522
22523     // private
22524     defaultMode: "drop",
22525
22526     /**
22527      * Displays the shadow under the target element
22528      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22529      */
22530     show : function(target){
22531         target = Roo.get(target);
22532         if(!this.el){
22533             this.el = Roo.Shadow.Pool.pull();
22534             if(this.el.dom.nextSibling != target.dom){
22535                 this.el.insertBefore(target);
22536             }
22537         }
22538         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22539         if(Roo.isIE){
22540             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22541         }
22542         this.realign(
22543             target.getLeft(true),
22544             target.getTop(true),
22545             target.getWidth(),
22546             target.getHeight()
22547         );
22548         this.el.dom.style.display = "block";
22549     },
22550
22551     /**
22552      * Returns true if the shadow is visible, else false
22553      */
22554     isVisible : function(){
22555         return this.el ? true : false;  
22556     },
22557
22558     /**
22559      * Direct alignment when values are already available. Show must be called at least once before
22560      * calling this method to ensure it is initialized.
22561      * @param {Number} left The target element left position
22562      * @param {Number} top The target element top position
22563      * @param {Number} width The target element width
22564      * @param {Number} height The target element height
22565      */
22566     realign : function(l, t, w, h){
22567         if(!this.el){
22568             return;
22569         }
22570         var a = this.adjusts, d = this.el.dom, s = d.style;
22571         var iea = 0;
22572         s.left = (l+a.l)+"px";
22573         s.top = (t+a.t)+"px";
22574         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22575  
22576         if(s.width != sws || s.height != shs){
22577             s.width = sws;
22578             s.height = shs;
22579             if(!Roo.isIE){
22580                 var cn = d.childNodes;
22581                 var sww = Math.max(0, (sw-12))+"px";
22582                 cn[0].childNodes[1].style.width = sww;
22583                 cn[1].childNodes[1].style.width = sww;
22584                 cn[2].childNodes[1].style.width = sww;
22585                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22586             }
22587         }
22588     },
22589
22590     /**
22591      * Hides this shadow
22592      */
22593     hide : function(){
22594         if(this.el){
22595             this.el.dom.style.display = "none";
22596             Roo.Shadow.Pool.push(this.el);
22597             delete this.el;
22598         }
22599     },
22600
22601     /**
22602      * Adjust the z-index of this shadow
22603      * @param {Number} zindex The new z-index
22604      */
22605     setZIndex : function(z){
22606         this.zIndex = z;
22607         if(this.el){
22608             this.el.setStyle("z-index", z);
22609         }
22610     }
22611 };
22612
22613 // Private utility class that manages the internal Shadow cache
22614 Roo.Shadow.Pool = function(){
22615     var p = [];
22616     var markup = Roo.isIE ?
22617                  '<div class="x-ie-shadow"></div>' :
22618                  '<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>';
22619     return {
22620         pull : function(){
22621             var sh = p.shift();
22622             if(!sh){
22623                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22624                 sh.autoBoxAdjust = false;
22625             }
22626             return sh;
22627         },
22628
22629         push : function(sh){
22630             p.push(sh);
22631         }
22632     };
22633 }();/*
22634  * Based on:
22635  * Ext JS Library 1.1.1
22636  * Copyright(c) 2006-2007, Ext JS, LLC.
22637  *
22638  * Originally Released Under LGPL - original licence link has changed is not relivant.
22639  *
22640  * Fork - LGPL
22641  * <script type="text/javascript">
22642  */
22643
22644 /**
22645  * @class Roo.BoxComponent
22646  * @extends Roo.Component
22647  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22648  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22649  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22650  * layout containers.
22651  * @constructor
22652  * @param {Roo.Element/String/Object} config The configuration options.
22653  */
22654 Roo.BoxComponent = function(config){
22655     Roo.Component.call(this, config);
22656     this.addEvents({
22657         /**
22658          * @event resize
22659          * Fires after the component is resized.
22660              * @param {Roo.Component} this
22661              * @param {Number} adjWidth The box-adjusted width that was set
22662              * @param {Number} adjHeight The box-adjusted height that was set
22663              * @param {Number} rawWidth The width that was originally specified
22664              * @param {Number} rawHeight The height that was originally specified
22665              */
22666         resize : true,
22667         /**
22668          * @event move
22669          * Fires after the component is moved.
22670              * @param {Roo.Component} this
22671              * @param {Number} x The new x position
22672              * @param {Number} y The new y position
22673              */
22674         move : true
22675     });
22676 };
22677
22678 Roo.extend(Roo.BoxComponent, Roo.Component, {
22679     // private, set in afterRender to signify that the component has been rendered
22680     boxReady : false,
22681     // private, used to defer height settings to subclasses
22682     deferHeight: false,
22683     /** @cfg {Number} width
22684      * width (optional) size of component
22685      */
22686      /** @cfg {Number} height
22687      * height (optional) size of component
22688      */
22689      
22690     /**
22691      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22692      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22693      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22694      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22695      * @return {Roo.BoxComponent} this
22696      */
22697     setSize : function(w, h){
22698         // support for standard size objects
22699         if(typeof w == 'object'){
22700             h = w.height;
22701             w = w.width;
22702         }
22703         // not rendered
22704         if(!this.boxReady){
22705             this.width = w;
22706             this.height = h;
22707             return this;
22708         }
22709
22710         // prevent recalcs when not needed
22711         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22712             return this;
22713         }
22714         this.lastSize = {width: w, height: h};
22715
22716         var adj = this.adjustSize(w, h);
22717         var aw = adj.width, ah = adj.height;
22718         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22719             var rz = this.getResizeEl();
22720             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22721                 rz.setSize(aw, ah);
22722             }else if(!this.deferHeight && ah !== undefined){
22723                 rz.setHeight(ah);
22724             }else if(aw !== undefined){
22725                 rz.setWidth(aw);
22726             }
22727             this.onResize(aw, ah, w, h);
22728             this.fireEvent('resize', this, aw, ah, w, h);
22729         }
22730         return this;
22731     },
22732
22733     /**
22734      * Gets the current size of the component's underlying element.
22735      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22736      */
22737     getSize : function(){
22738         return this.el.getSize();
22739     },
22740
22741     /**
22742      * Gets the current XY position of the component's underlying element.
22743      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22744      * @return {Array} The XY position of the element (e.g., [100, 200])
22745      */
22746     getPosition : function(local){
22747         if(local === true){
22748             return [this.el.getLeft(true), this.el.getTop(true)];
22749         }
22750         return this.xy || this.el.getXY();
22751     },
22752
22753     /**
22754      * Gets the current box measurements of the component's underlying element.
22755      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22756      * @returns {Object} box An object in the format {x, y, width, height}
22757      */
22758     getBox : function(local){
22759         var s = this.el.getSize();
22760         if(local){
22761             s.x = this.el.getLeft(true);
22762             s.y = this.el.getTop(true);
22763         }else{
22764             var xy = this.xy || this.el.getXY();
22765             s.x = xy[0];
22766             s.y = xy[1];
22767         }
22768         return s;
22769     },
22770
22771     /**
22772      * Sets the current box measurements of the component's underlying element.
22773      * @param {Object} box An object in the format {x, y, width, height}
22774      * @returns {Roo.BoxComponent} this
22775      */
22776     updateBox : function(box){
22777         this.setSize(box.width, box.height);
22778         this.setPagePosition(box.x, box.y);
22779         return this;
22780     },
22781
22782     // protected
22783     getResizeEl : function(){
22784         return this.resizeEl || this.el;
22785     },
22786
22787     // protected
22788     getPositionEl : function(){
22789         return this.positionEl || this.el;
22790     },
22791
22792     /**
22793      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22794      * This method fires the move event.
22795      * @param {Number} left The new left
22796      * @param {Number} top The new top
22797      * @returns {Roo.BoxComponent} this
22798      */
22799     setPosition : function(x, y){
22800         this.x = x;
22801         this.y = y;
22802         if(!this.boxReady){
22803             return this;
22804         }
22805         var adj = this.adjustPosition(x, y);
22806         var ax = adj.x, ay = adj.y;
22807
22808         var el = this.getPositionEl();
22809         if(ax !== undefined || ay !== undefined){
22810             if(ax !== undefined && ay !== undefined){
22811                 el.setLeftTop(ax, ay);
22812             }else if(ax !== undefined){
22813                 el.setLeft(ax);
22814             }else if(ay !== undefined){
22815                 el.setTop(ay);
22816             }
22817             this.onPosition(ax, ay);
22818             this.fireEvent('move', this, ax, ay);
22819         }
22820         return this;
22821     },
22822
22823     /**
22824      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22825      * This method fires the move event.
22826      * @param {Number} x The new x position
22827      * @param {Number} y The new y position
22828      * @returns {Roo.BoxComponent} this
22829      */
22830     setPagePosition : function(x, y){
22831         this.pageX = x;
22832         this.pageY = y;
22833         if(!this.boxReady){
22834             return;
22835         }
22836         if(x === undefined || y === undefined){ // cannot translate undefined points
22837             return;
22838         }
22839         var p = this.el.translatePoints(x, y);
22840         this.setPosition(p.left, p.top);
22841         return this;
22842     },
22843
22844     // private
22845     onRender : function(ct, position){
22846         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22847         if(this.resizeEl){
22848             this.resizeEl = Roo.get(this.resizeEl);
22849         }
22850         if(this.positionEl){
22851             this.positionEl = Roo.get(this.positionEl);
22852         }
22853     },
22854
22855     // private
22856     afterRender : function(){
22857         Roo.BoxComponent.superclass.afterRender.call(this);
22858         this.boxReady = true;
22859         this.setSize(this.width, this.height);
22860         if(this.x || this.y){
22861             this.setPosition(this.x, this.y);
22862         }
22863         if(this.pageX || this.pageY){
22864             this.setPagePosition(this.pageX, this.pageY);
22865         }
22866     },
22867
22868     /**
22869      * Force the component's size to recalculate based on the underlying element's current height and width.
22870      * @returns {Roo.BoxComponent} this
22871      */
22872     syncSize : function(){
22873         delete this.lastSize;
22874         this.setSize(this.el.getWidth(), this.el.getHeight());
22875         return this;
22876     },
22877
22878     /**
22879      * Called after the component is resized, this method is empty by default but can be implemented by any
22880      * subclass that needs to perform custom logic after a resize occurs.
22881      * @param {Number} adjWidth The box-adjusted width that was set
22882      * @param {Number} adjHeight The box-adjusted height that was set
22883      * @param {Number} rawWidth The width that was originally specified
22884      * @param {Number} rawHeight The height that was originally specified
22885      */
22886     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22887
22888     },
22889
22890     /**
22891      * Called after the component is moved, this method is empty by default but can be implemented by any
22892      * subclass that needs to perform custom logic after a move occurs.
22893      * @param {Number} x The new x position
22894      * @param {Number} y The new y position
22895      */
22896     onPosition : function(x, y){
22897
22898     },
22899
22900     // private
22901     adjustSize : function(w, h){
22902         if(this.autoWidth){
22903             w = 'auto';
22904         }
22905         if(this.autoHeight){
22906             h = 'auto';
22907         }
22908         return {width : w, height: h};
22909     },
22910
22911     // private
22912     adjustPosition : function(x, y){
22913         return {x : x, y: y};
22914     }
22915 });/*
22916  * Based on:
22917  * Ext JS Library 1.1.1
22918  * Copyright(c) 2006-2007, Ext JS, LLC.
22919  *
22920  * Originally Released Under LGPL - original licence link has changed is not relivant.
22921  *
22922  * Fork - LGPL
22923  * <script type="text/javascript">
22924  */
22925
22926
22927 /**
22928  * @class Roo.SplitBar
22929  * @extends Roo.util.Observable
22930  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22931  * <br><br>
22932  * Usage:
22933  * <pre><code>
22934 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22935                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22936 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22937 split.minSize = 100;
22938 split.maxSize = 600;
22939 split.animate = true;
22940 split.on('moved', splitterMoved);
22941 </code></pre>
22942  * @constructor
22943  * Create a new SplitBar
22944  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22945  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22946  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22947  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22948                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22949                         position of the SplitBar).
22950  */
22951 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22952     
22953     /** @private */
22954     this.el = Roo.get(dragElement, true);
22955     this.el.dom.unselectable = "on";
22956     /** @private */
22957     this.resizingEl = Roo.get(resizingElement, true);
22958
22959     /**
22960      * @private
22961      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22962      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22963      * @type Number
22964      */
22965     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22966     
22967     /**
22968      * The minimum size of the resizing element. (Defaults to 0)
22969      * @type Number
22970      */
22971     this.minSize = 0;
22972     
22973     /**
22974      * The maximum size of the resizing element. (Defaults to 2000)
22975      * @type Number
22976      */
22977     this.maxSize = 2000;
22978     
22979     /**
22980      * Whether to animate the transition to the new size
22981      * @type Boolean
22982      */
22983     this.animate = false;
22984     
22985     /**
22986      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22987      * @type Boolean
22988      */
22989     this.useShim = false;
22990     
22991     /** @private */
22992     this.shim = null;
22993     
22994     if(!existingProxy){
22995         /** @private */
22996         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22997     }else{
22998         this.proxy = Roo.get(existingProxy).dom;
22999     }
23000     /** @private */
23001     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23002     
23003     /** @private */
23004     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23005     
23006     /** @private */
23007     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23008     
23009     /** @private */
23010     this.dragSpecs = {};
23011     
23012     /**
23013      * @private The adapter to use to positon and resize elements
23014      */
23015     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23016     this.adapter.init(this);
23017     
23018     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23019         /** @private */
23020         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23021         this.el.addClass("x-splitbar-h");
23022     }else{
23023         /** @private */
23024         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23025         this.el.addClass("x-splitbar-v");
23026     }
23027     
23028     this.addEvents({
23029         /**
23030          * @event resize
23031          * Fires when the splitter is moved (alias for {@link #event-moved})
23032          * @param {Roo.SplitBar} this
23033          * @param {Number} newSize the new width or height
23034          */
23035         "resize" : true,
23036         /**
23037          * @event moved
23038          * Fires when the splitter is moved
23039          * @param {Roo.SplitBar} this
23040          * @param {Number} newSize the new width or height
23041          */
23042         "moved" : true,
23043         /**
23044          * @event beforeresize
23045          * Fires before the splitter is dragged
23046          * @param {Roo.SplitBar} this
23047          */
23048         "beforeresize" : true,
23049
23050         "beforeapply" : true
23051     });
23052
23053     Roo.util.Observable.call(this);
23054 };
23055
23056 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23057     onStartProxyDrag : function(x, y){
23058         this.fireEvent("beforeresize", this);
23059         if(!this.overlay){
23060             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23061             o.unselectable();
23062             o.enableDisplayMode("block");
23063             // all splitbars share the same overlay
23064             Roo.SplitBar.prototype.overlay = o;
23065         }
23066         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23067         this.overlay.show();
23068         Roo.get(this.proxy).setDisplayed("block");
23069         var size = this.adapter.getElementSize(this);
23070         this.activeMinSize = this.getMinimumSize();;
23071         this.activeMaxSize = this.getMaximumSize();;
23072         var c1 = size - this.activeMinSize;
23073         var c2 = Math.max(this.activeMaxSize - size, 0);
23074         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23075             this.dd.resetConstraints();
23076             this.dd.setXConstraint(
23077                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23078                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23079             );
23080             this.dd.setYConstraint(0, 0);
23081         }else{
23082             this.dd.resetConstraints();
23083             this.dd.setXConstraint(0, 0);
23084             this.dd.setYConstraint(
23085                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23086                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23087             );
23088          }
23089         this.dragSpecs.startSize = size;
23090         this.dragSpecs.startPoint = [x, y];
23091         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23092     },
23093     
23094     /** 
23095      * @private Called after the drag operation by the DDProxy
23096      */
23097     onEndProxyDrag : function(e){
23098         Roo.get(this.proxy).setDisplayed(false);
23099         var endPoint = Roo.lib.Event.getXY(e);
23100         if(this.overlay){
23101             this.overlay.hide();
23102         }
23103         var newSize;
23104         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23105             newSize = this.dragSpecs.startSize + 
23106                 (this.placement == Roo.SplitBar.LEFT ?
23107                     endPoint[0] - this.dragSpecs.startPoint[0] :
23108                     this.dragSpecs.startPoint[0] - endPoint[0]
23109                 );
23110         }else{
23111             newSize = this.dragSpecs.startSize + 
23112                 (this.placement == Roo.SplitBar.TOP ?
23113                     endPoint[1] - this.dragSpecs.startPoint[1] :
23114                     this.dragSpecs.startPoint[1] - endPoint[1]
23115                 );
23116         }
23117         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23118         if(newSize != this.dragSpecs.startSize){
23119             if(this.fireEvent('beforeapply', this, newSize) !== false){
23120                 this.adapter.setElementSize(this, newSize);
23121                 this.fireEvent("moved", this, newSize);
23122                 this.fireEvent("resize", this, newSize);
23123             }
23124         }
23125     },
23126     
23127     /**
23128      * Get the adapter this SplitBar uses
23129      * @return The adapter object
23130      */
23131     getAdapter : function(){
23132         return this.adapter;
23133     },
23134     
23135     /**
23136      * Set the adapter this SplitBar uses
23137      * @param {Object} adapter A SplitBar adapter object
23138      */
23139     setAdapter : function(adapter){
23140         this.adapter = adapter;
23141         this.adapter.init(this);
23142     },
23143     
23144     /**
23145      * Gets the minimum size for the resizing element
23146      * @return {Number} The minimum size
23147      */
23148     getMinimumSize : function(){
23149         return this.minSize;
23150     },
23151     
23152     /**
23153      * Sets the minimum size for the resizing element
23154      * @param {Number} minSize The minimum size
23155      */
23156     setMinimumSize : function(minSize){
23157         this.minSize = minSize;
23158     },
23159     
23160     /**
23161      * Gets the maximum size for the resizing element
23162      * @return {Number} The maximum size
23163      */
23164     getMaximumSize : function(){
23165         return this.maxSize;
23166     },
23167     
23168     /**
23169      * Sets the maximum size for the resizing element
23170      * @param {Number} maxSize The maximum size
23171      */
23172     setMaximumSize : function(maxSize){
23173         this.maxSize = maxSize;
23174     },
23175     
23176     /**
23177      * Sets the initialize size for the resizing element
23178      * @param {Number} size The initial size
23179      */
23180     setCurrentSize : function(size){
23181         var oldAnimate = this.animate;
23182         this.animate = false;
23183         this.adapter.setElementSize(this, size);
23184         this.animate = oldAnimate;
23185     },
23186     
23187     /**
23188      * Destroy this splitbar. 
23189      * @param {Boolean} removeEl True to remove the element
23190      */
23191     destroy : function(removeEl){
23192         if(this.shim){
23193             this.shim.remove();
23194         }
23195         this.dd.unreg();
23196         this.proxy.parentNode.removeChild(this.proxy);
23197         if(removeEl){
23198             this.el.remove();
23199         }
23200     }
23201 });
23202
23203 /**
23204  * @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.
23205  */
23206 Roo.SplitBar.createProxy = function(dir){
23207     var proxy = new Roo.Element(document.createElement("div"));
23208     proxy.unselectable();
23209     var cls = 'x-splitbar-proxy';
23210     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23211     document.body.appendChild(proxy.dom);
23212     return proxy.dom;
23213 };
23214
23215 /** 
23216  * @class Roo.SplitBar.BasicLayoutAdapter
23217  * Default Adapter. It assumes the splitter and resizing element are not positioned
23218  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23219  */
23220 Roo.SplitBar.BasicLayoutAdapter = function(){
23221 };
23222
23223 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23224     // do nothing for now
23225     init : function(s){
23226     
23227     },
23228     /**
23229      * Called before drag operations to get the current size of the resizing element. 
23230      * @param {Roo.SplitBar} s The SplitBar using this adapter
23231      */
23232      getElementSize : function(s){
23233         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23234             return s.resizingEl.getWidth();
23235         }else{
23236             return s.resizingEl.getHeight();
23237         }
23238     },
23239     
23240     /**
23241      * Called after drag operations to set the size of the resizing element.
23242      * @param {Roo.SplitBar} s The SplitBar using this adapter
23243      * @param {Number} newSize The new size to set
23244      * @param {Function} onComplete A function to be invoked when resizing is complete
23245      */
23246     setElementSize : function(s, newSize, onComplete){
23247         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23248             if(!s.animate){
23249                 s.resizingEl.setWidth(newSize);
23250                 if(onComplete){
23251                     onComplete(s, newSize);
23252                 }
23253             }else{
23254                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23255             }
23256         }else{
23257             
23258             if(!s.animate){
23259                 s.resizingEl.setHeight(newSize);
23260                 if(onComplete){
23261                     onComplete(s, newSize);
23262                 }
23263             }else{
23264                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23265             }
23266         }
23267     }
23268 };
23269
23270 /** 
23271  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23272  * @extends Roo.SplitBar.BasicLayoutAdapter
23273  * Adapter that  moves the splitter element to align with the resized sizing element. 
23274  * Used with an absolute positioned SplitBar.
23275  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23276  * document.body, make sure you assign an id to the body element.
23277  */
23278 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23279     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23280     this.container = Roo.get(container);
23281 };
23282
23283 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23284     init : function(s){
23285         this.basic.init(s);
23286     },
23287     
23288     getElementSize : function(s){
23289         return this.basic.getElementSize(s);
23290     },
23291     
23292     setElementSize : function(s, newSize, onComplete){
23293         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23294     },
23295     
23296     moveSplitter : function(s){
23297         var yes = Roo.SplitBar;
23298         switch(s.placement){
23299             case yes.LEFT:
23300                 s.el.setX(s.resizingEl.getRight());
23301                 break;
23302             case yes.RIGHT:
23303                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23304                 break;
23305             case yes.TOP:
23306                 s.el.setY(s.resizingEl.getBottom());
23307                 break;
23308             case yes.BOTTOM:
23309                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23310                 break;
23311         }
23312     }
23313 };
23314
23315 /**
23316  * Orientation constant - Create a vertical SplitBar
23317  * @static
23318  * @type Number
23319  */
23320 Roo.SplitBar.VERTICAL = 1;
23321
23322 /**
23323  * Orientation constant - Create a horizontal SplitBar
23324  * @static
23325  * @type Number
23326  */
23327 Roo.SplitBar.HORIZONTAL = 2;
23328
23329 /**
23330  * Placement constant - The resizing element is to the left of the splitter element
23331  * @static
23332  * @type Number
23333  */
23334 Roo.SplitBar.LEFT = 1;
23335
23336 /**
23337  * Placement constant - The resizing element is to the right of the splitter element
23338  * @static
23339  * @type Number
23340  */
23341 Roo.SplitBar.RIGHT = 2;
23342
23343 /**
23344  * Placement constant - The resizing element is positioned above the splitter element
23345  * @static
23346  * @type Number
23347  */
23348 Roo.SplitBar.TOP = 3;
23349
23350 /**
23351  * Placement constant - The resizing element is positioned under splitter element
23352  * @static
23353  * @type Number
23354  */
23355 Roo.SplitBar.BOTTOM = 4;
23356 /*
23357  * Based on:
23358  * Ext JS Library 1.1.1
23359  * Copyright(c) 2006-2007, Ext JS, LLC.
23360  *
23361  * Originally Released Under LGPL - original licence link has changed is not relivant.
23362  *
23363  * Fork - LGPL
23364  * <script type="text/javascript">
23365  */
23366
23367 /**
23368  * @class Roo.View
23369  * @extends Roo.util.Observable
23370  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23371  * This class also supports single and multi selection modes. <br>
23372  * Create a data model bound view:
23373  <pre><code>
23374  var store = new Roo.data.Store(...);
23375
23376  var view = new Roo.View({
23377     el : "my-element",
23378     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23379  
23380     singleSelect: true,
23381     selectedClass: "ydataview-selected",
23382     store: store
23383  });
23384
23385  // listen for node click?
23386  view.on("click", function(vw, index, node, e){
23387  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23388  });
23389
23390  // load XML data
23391  dataModel.load("foobar.xml");
23392  </code></pre>
23393  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23394  * <br><br>
23395  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23396  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23397  * 
23398  * Note: old style constructor is still suported (container, template, config)
23399  * 
23400  * @constructor
23401  * Create a new View
23402  * @param {Object} config The config object
23403  * 
23404  */
23405 Roo.View = function(config, depreciated_tpl, depreciated_config){
23406     
23407     if (typeof(depreciated_tpl) == 'undefined') {
23408         // new way.. - universal constructor.
23409         Roo.apply(this, config);
23410         this.el  = Roo.get(this.el);
23411     } else {
23412         // old format..
23413         this.el  = Roo.get(config);
23414         this.tpl = depreciated_tpl;
23415         Roo.apply(this, depreciated_config);
23416     }
23417      
23418     
23419     if(typeof(this.tpl) == "string"){
23420         this.tpl = new Roo.Template(this.tpl);
23421     } else {
23422         // support xtype ctors..
23423         this.tpl = new Roo.factory(this.tpl, Roo);
23424     }
23425     
23426     
23427     this.tpl.compile();
23428    
23429
23430      
23431     /** @private */
23432     this.addEvents({
23433     /**
23434      * @event beforeclick
23435      * Fires before a click is processed. Returns false to cancel the default action.
23436      * @param {Roo.View} this
23437      * @param {Number} index The index of the target node
23438      * @param {HTMLElement} node The target node
23439      * @param {Roo.EventObject} e The raw event object
23440      */
23441         "beforeclick" : true,
23442     /**
23443      * @event click
23444      * Fires when a template node is clicked.
23445      * @param {Roo.View} this
23446      * @param {Number} index The index of the target node
23447      * @param {HTMLElement} node The target node
23448      * @param {Roo.EventObject} e The raw event object
23449      */
23450         "click" : true,
23451     /**
23452      * @event dblclick
23453      * Fires when a template node is double clicked.
23454      * @param {Roo.View} this
23455      * @param {Number} index The index of the target node
23456      * @param {HTMLElement} node The target node
23457      * @param {Roo.EventObject} e The raw event object
23458      */
23459         "dblclick" : true,
23460     /**
23461      * @event contextmenu
23462      * Fires when a template node is right clicked.
23463      * @param {Roo.View} this
23464      * @param {Number} index The index of the target node
23465      * @param {HTMLElement} node The target node
23466      * @param {Roo.EventObject} e The raw event object
23467      */
23468         "contextmenu" : true,
23469     /**
23470      * @event selectionchange
23471      * Fires when the selected nodes change.
23472      * @param {Roo.View} this
23473      * @param {Array} selections Array of the selected nodes
23474      */
23475         "selectionchange" : true,
23476
23477     /**
23478      * @event beforeselect
23479      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23480      * @param {Roo.View} this
23481      * @param {HTMLElement} node The node to be selected
23482      * @param {Array} selections Array of currently selected nodes
23483      */
23484         "beforeselect" : true
23485     });
23486
23487     this.el.on({
23488         "click": this.onClick,
23489         "dblclick": this.onDblClick,
23490         "contextmenu": this.onContextMenu,
23491         scope:this
23492     });
23493
23494     this.selections = [];
23495     this.nodes = [];
23496     this.cmp = new Roo.CompositeElementLite([]);
23497     if(this.store){
23498         this.store = Roo.factory(this.store, Roo.data);
23499         this.setStore(this.store, true);
23500     }
23501     Roo.View.superclass.constructor.call(this);
23502 };
23503
23504 Roo.extend(Roo.View, Roo.util.Observable, {
23505     
23506      /**
23507      * @cfg {Roo.data.Store} store Data store to load data from.
23508      */
23509     store : false,
23510     
23511     /**
23512      * @cfg {String|Roo.Element} el The container element.
23513      */
23514     el : '',
23515     
23516     /**
23517      * @cfg {String|Roo.Template} tpl The template used by this View 
23518      */
23519     tpl : false,
23520     
23521     /**
23522      * @cfg {String} selectedClass The css class to add to selected nodes
23523      */
23524     selectedClass : "x-view-selected",
23525      /**
23526      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23527      */
23528     emptyText : "",
23529     /**
23530      * @cfg {Boolean} multiSelect Allow multiple selection
23531      */
23532     
23533     multiSelect : false,
23534     /**
23535      * @cfg {Boolean} singleSelect Allow single selection
23536      */
23537     singleSelect:  false,
23538     
23539     /**
23540      * Returns the element this view is bound to.
23541      * @return {Roo.Element}
23542      */
23543     getEl : function(){
23544         return this.el;
23545     },
23546
23547     /**
23548      * Refreshes the view.
23549      */
23550     refresh : function(){
23551         var t = this.tpl;
23552         this.clearSelections();
23553         this.el.update("");
23554         var html = [];
23555         var records = this.store.getRange();
23556         if(records.length < 1){
23557             this.el.update(this.emptyText);
23558             return;
23559         }
23560         for(var i = 0, len = records.length; i < len; i++){
23561             var data = this.prepareData(records[i].data, i, records[i]);
23562             html[html.length] = t.apply(data);
23563         }
23564         this.el.update(html.join(""));
23565         this.nodes = this.el.dom.childNodes;
23566         this.updateIndexes(0);
23567     },
23568
23569     /**
23570      * Function to override to reformat the data that is sent to
23571      * the template for each node.
23572      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23573      * a JSON object for an UpdateManager bound view).
23574      */
23575     prepareData : function(data){
23576         return data;
23577     },
23578
23579     onUpdate : function(ds, record){
23580         this.clearSelections();
23581         var index = this.store.indexOf(record);
23582         var n = this.nodes[index];
23583         this.tpl.insertBefore(n, this.prepareData(record.data));
23584         n.parentNode.removeChild(n);
23585         this.updateIndexes(index, index);
23586     },
23587
23588     onAdd : function(ds, records, index){
23589         this.clearSelections();
23590         if(this.nodes.length == 0){
23591             this.refresh();
23592             return;
23593         }
23594         var n = this.nodes[index];
23595         for(var i = 0, len = records.length; i < len; i++){
23596             var d = this.prepareData(records[i].data);
23597             if(n){
23598                 this.tpl.insertBefore(n, d);
23599             }else{
23600                 this.tpl.append(this.el, d);
23601             }
23602         }
23603         this.updateIndexes(index);
23604     },
23605
23606     onRemove : function(ds, record, index){
23607         this.clearSelections();
23608         this.el.dom.removeChild(this.nodes[index]);
23609         this.updateIndexes(index);
23610     },
23611
23612     /**
23613      * Refresh an individual node.
23614      * @param {Number} index
23615      */
23616     refreshNode : function(index){
23617         this.onUpdate(this.store, this.store.getAt(index));
23618     },
23619
23620     updateIndexes : function(startIndex, endIndex){
23621         var ns = this.nodes;
23622         startIndex = startIndex || 0;
23623         endIndex = endIndex || ns.length - 1;
23624         for(var i = startIndex; i <= endIndex; i++){
23625             ns[i].nodeIndex = i;
23626         }
23627     },
23628
23629     /**
23630      * Changes the data store this view uses and refresh the view.
23631      * @param {Store} store
23632      */
23633     setStore : function(store, initial){
23634         if(!initial && this.store){
23635             this.store.un("datachanged", this.refresh);
23636             this.store.un("add", this.onAdd);
23637             this.store.un("remove", this.onRemove);
23638             this.store.un("update", this.onUpdate);
23639             this.store.un("clear", this.refresh);
23640         }
23641         if(store){
23642           
23643             store.on("datachanged", this.refresh, this);
23644             store.on("add", this.onAdd, this);
23645             store.on("remove", this.onRemove, this);
23646             store.on("update", this.onUpdate, this);
23647             store.on("clear", this.refresh, this);
23648         }
23649         
23650         if(store){
23651             this.refresh();
23652         }
23653     },
23654
23655     /**
23656      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23657      * @param {HTMLElement} node
23658      * @return {HTMLElement} The template node
23659      */
23660     findItemFromChild : function(node){
23661         var el = this.el.dom;
23662         if(!node || node.parentNode == el){
23663                     return node;
23664             }
23665             var p = node.parentNode;
23666             while(p && p != el){
23667             if(p.parentNode == el){
23668                 return p;
23669             }
23670             p = p.parentNode;
23671         }
23672             return null;
23673     },
23674
23675     /** @ignore */
23676     onClick : function(e){
23677         var item = this.findItemFromChild(e.getTarget());
23678         if(item){
23679             var index = this.indexOf(item);
23680             if(this.onItemClick(item, index, e) !== false){
23681                 this.fireEvent("click", this, index, item, e);
23682             }
23683         }else{
23684             this.clearSelections();
23685         }
23686     },
23687
23688     /** @ignore */
23689     onContextMenu : function(e){
23690         var item = this.findItemFromChild(e.getTarget());
23691         if(item){
23692             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23693         }
23694     },
23695
23696     /** @ignore */
23697     onDblClick : function(e){
23698         var item = this.findItemFromChild(e.getTarget());
23699         if(item){
23700             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23701         }
23702     },
23703
23704     onItemClick : function(item, index, e){
23705         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23706             return false;
23707         }
23708         if(this.multiSelect || this.singleSelect){
23709             if(this.multiSelect && e.shiftKey && this.lastSelection){
23710                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23711             }else{
23712                 this.select(item, this.multiSelect && e.ctrlKey);
23713                 this.lastSelection = item;
23714             }
23715             e.preventDefault();
23716         }
23717         return true;
23718     },
23719
23720     /**
23721      * Get the number of selected nodes.
23722      * @return {Number}
23723      */
23724     getSelectionCount : function(){
23725         return this.selections.length;
23726     },
23727
23728     /**
23729      * Get the currently selected nodes.
23730      * @return {Array} An array of HTMLElements
23731      */
23732     getSelectedNodes : function(){
23733         return this.selections;
23734     },
23735
23736     /**
23737      * Get the indexes of the selected nodes.
23738      * @return {Array}
23739      */
23740     getSelectedIndexes : function(){
23741         var indexes = [], s = this.selections;
23742         for(var i = 0, len = s.length; i < len; i++){
23743             indexes.push(s[i].nodeIndex);
23744         }
23745         return indexes;
23746     },
23747
23748     /**
23749      * Clear all selections
23750      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23751      */
23752     clearSelections : function(suppressEvent){
23753         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23754             this.cmp.elements = this.selections;
23755             this.cmp.removeClass(this.selectedClass);
23756             this.selections = [];
23757             if(!suppressEvent){
23758                 this.fireEvent("selectionchange", this, this.selections);
23759             }
23760         }
23761     },
23762
23763     /**
23764      * Returns true if the passed node is selected
23765      * @param {HTMLElement/Number} node The node or node index
23766      * @return {Boolean}
23767      */
23768     isSelected : function(node){
23769         var s = this.selections;
23770         if(s.length < 1){
23771             return false;
23772         }
23773         node = this.getNode(node);
23774         return s.indexOf(node) !== -1;
23775     },
23776
23777     /**
23778      * Selects nodes.
23779      * @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
23780      * @param {Boolean} keepExisting (optional) true to keep existing selections
23781      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23782      */
23783     select : function(nodeInfo, keepExisting, suppressEvent){
23784         if(nodeInfo instanceof Array){
23785             if(!keepExisting){
23786                 this.clearSelections(true);
23787             }
23788             for(var i = 0, len = nodeInfo.length; i < len; i++){
23789                 this.select(nodeInfo[i], true, true);
23790             }
23791         } else{
23792             var node = this.getNode(nodeInfo);
23793             if(node && !this.isSelected(node)){
23794                 if(!keepExisting){
23795                     this.clearSelections(true);
23796                 }
23797                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23798                     Roo.fly(node).addClass(this.selectedClass);
23799                     this.selections.push(node);
23800                     if(!suppressEvent){
23801                         this.fireEvent("selectionchange", this, this.selections);
23802                     }
23803                 }
23804             }
23805         }
23806     },
23807
23808     /**
23809      * Gets a template node.
23810      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23811      * @return {HTMLElement} The node or null if it wasn't found
23812      */
23813     getNode : function(nodeInfo){
23814         if(typeof nodeInfo == "string"){
23815             return document.getElementById(nodeInfo);
23816         }else if(typeof nodeInfo == "number"){
23817             return this.nodes[nodeInfo];
23818         }
23819         return nodeInfo;
23820     },
23821
23822     /**
23823      * Gets a range template nodes.
23824      * @param {Number} startIndex
23825      * @param {Number} endIndex
23826      * @return {Array} An array of nodes
23827      */
23828     getNodes : function(start, end){
23829         var ns = this.nodes;
23830         start = start || 0;
23831         end = typeof end == "undefined" ? ns.length - 1 : end;
23832         var nodes = [];
23833         if(start <= end){
23834             for(var i = start; i <= end; i++){
23835                 nodes.push(ns[i]);
23836             }
23837         } else{
23838             for(var i = start; i >= end; i--){
23839                 nodes.push(ns[i]);
23840             }
23841         }
23842         return nodes;
23843     },
23844
23845     /**
23846      * Finds the index of the passed node
23847      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23848      * @return {Number} The index of the node or -1
23849      */
23850     indexOf : function(node){
23851         node = this.getNode(node);
23852         if(typeof node.nodeIndex == "number"){
23853             return node.nodeIndex;
23854         }
23855         var ns = this.nodes;
23856         for(var i = 0, len = ns.length; i < len; i++){
23857             if(ns[i] == node){
23858                 return i;
23859             }
23860         }
23861         return -1;
23862     }
23863 });
23864 /*
23865  * Based on:
23866  * Ext JS Library 1.1.1
23867  * Copyright(c) 2006-2007, Ext JS, LLC.
23868  *
23869  * Originally Released Under LGPL - original licence link has changed is not relivant.
23870  *
23871  * Fork - LGPL
23872  * <script type="text/javascript">
23873  */
23874
23875 /**
23876  * @class Roo.JsonView
23877  * @extends Roo.View
23878  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23879 <pre><code>
23880 var view = new Roo.JsonView({
23881     container: "my-element",
23882     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23883     multiSelect: true, 
23884     jsonRoot: "data" 
23885 });
23886
23887 // listen for node click?
23888 view.on("click", function(vw, index, node, e){
23889     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23890 });
23891
23892 // direct load of JSON data
23893 view.load("foobar.php");
23894
23895 // Example from my blog list
23896 var tpl = new Roo.Template(
23897     '&lt;div class="entry"&gt;' +
23898     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23899     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23900     "&lt;/div&gt;&lt;hr /&gt;"
23901 );
23902
23903 var moreView = new Roo.JsonView({
23904     container :  "entry-list", 
23905     template : tpl,
23906     jsonRoot: "posts"
23907 });
23908 moreView.on("beforerender", this.sortEntries, this);
23909 moreView.load({
23910     url: "/blog/get-posts.php",
23911     params: "allposts=true",
23912     text: "Loading Blog Entries..."
23913 });
23914 </code></pre>
23915
23916 * Note: old code is supported with arguments : (container, template, config)
23917
23918
23919  * @constructor
23920  * Create a new JsonView
23921  * 
23922  * @param {Object} config The config object
23923  * 
23924  */
23925 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23926     
23927     
23928     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23929
23930     var um = this.el.getUpdateManager();
23931     um.setRenderer(this);
23932     um.on("update", this.onLoad, this);
23933     um.on("failure", this.onLoadException, this);
23934
23935     /**
23936      * @event beforerender
23937      * Fires before rendering of the downloaded JSON data.
23938      * @param {Roo.JsonView} this
23939      * @param {Object} data The JSON data loaded
23940      */
23941     /**
23942      * @event load
23943      * Fires when data is loaded.
23944      * @param {Roo.JsonView} this
23945      * @param {Object} data The JSON data loaded
23946      * @param {Object} response The raw Connect response object
23947      */
23948     /**
23949      * @event loadexception
23950      * Fires when loading fails.
23951      * @param {Roo.JsonView} this
23952      * @param {Object} response The raw Connect response object
23953      */
23954     this.addEvents({
23955         'beforerender' : true,
23956         'load' : true,
23957         'loadexception' : true
23958     });
23959 };
23960 Roo.extend(Roo.JsonView, Roo.View, {
23961     /**
23962      * @type {String} The root property in the loaded JSON object that contains the data
23963      */
23964     jsonRoot : "",
23965
23966     /**
23967      * Refreshes the view.
23968      */
23969     refresh : function(){
23970         this.clearSelections();
23971         this.el.update("");
23972         var html = [];
23973         var o = this.jsonData;
23974         if(o && o.length > 0){
23975             for(var i = 0, len = o.length; i < len; i++){
23976                 var data = this.prepareData(o[i], i, o);
23977                 html[html.length] = this.tpl.apply(data);
23978             }
23979         }else{
23980             html.push(this.emptyText);
23981         }
23982         this.el.update(html.join(""));
23983         this.nodes = this.el.dom.childNodes;
23984         this.updateIndexes(0);
23985     },
23986
23987     /**
23988      * 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.
23989      * @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:
23990      <pre><code>
23991      view.load({
23992          url: "your-url.php",
23993          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23994          callback: yourFunction,
23995          scope: yourObject, //(optional scope)
23996          discardUrl: false,
23997          nocache: false,
23998          text: "Loading...",
23999          timeout: 30,
24000          scripts: false
24001      });
24002      </code></pre>
24003      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24004      * 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.
24005      * @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}
24006      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24007      * @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.
24008      */
24009     load : function(){
24010         var um = this.el.getUpdateManager();
24011         um.update.apply(um, arguments);
24012     },
24013
24014     render : function(el, response){
24015         this.clearSelections();
24016         this.el.update("");
24017         var o;
24018         try{
24019             o = Roo.util.JSON.decode(response.responseText);
24020             if(this.jsonRoot){
24021                 
24022                 o = o[this.jsonRoot];
24023             }
24024         } catch(e){
24025         }
24026         /**
24027          * The current JSON data or null
24028          */
24029         this.jsonData = o;
24030         this.beforeRender();
24031         this.refresh();
24032     },
24033
24034 /**
24035  * Get the number of records in the current JSON dataset
24036  * @return {Number}
24037  */
24038     getCount : function(){
24039         return this.jsonData ? this.jsonData.length : 0;
24040     },
24041
24042 /**
24043  * Returns the JSON object for the specified node(s)
24044  * @param {HTMLElement/Array} node The node or an array of nodes
24045  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24046  * you get the JSON object for the node
24047  */
24048     getNodeData : function(node){
24049         if(node instanceof Array){
24050             var data = [];
24051             for(var i = 0, len = node.length; i < len; i++){
24052                 data.push(this.getNodeData(node[i]));
24053             }
24054             return data;
24055         }
24056         return this.jsonData[this.indexOf(node)] || null;
24057     },
24058
24059     beforeRender : function(){
24060         this.snapshot = this.jsonData;
24061         if(this.sortInfo){
24062             this.sort.apply(this, this.sortInfo);
24063         }
24064         this.fireEvent("beforerender", this, this.jsonData);
24065     },
24066
24067     onLoad : function(el, o){
24068         this.fireEvent("load", this, this.jsonData, o);
24069     },
24070
24071     onLoadException : function(el, o){
24072         this.fireEvent("loadexception", this, o);
24073     },
24074
24075 /**
24076  * Filter the data by a specific property.
24077  * @param {String} property A property on your JSON objects
24078  * @param {String/RegExp} value Either string that the property values
24079  * should start with, or a RegExp to test against the property
24080  */
24081     filter : function(property, value){
24082         if(this.jsonData){
24083             var data = [];
24084             var ss = this.snapshot;
24085             if(typeof value == "string"){
24086                 var vlen = value.length;
24087                 if(vlen == 0){
24088                     this.clearFilter();
24089                     return;
24090                 }
24091                 value = value.toLowerCase();
24092                 for(var i = 0, len = ss.length; i < len; i++){
24093                     var o = ss[i];
24094                     if(o[property].substr(0, vlen).toLowerCase() == value){
24095                         data.push(o);
24096                     }
24097                 }
24098             } else if(value.exec){ // regex?
24099                 for(var i = 0, len = ss.length; i < len; i++){
24100                     var o = ss[i];
24101                     if(value.test(o[property])){
24102                         data.push(o);
24103                     }
24104                 }
24105             } else{
24106                 return;
24107             }
24108             this.jsonData = data;
24109             this.refresh();
24110         }
24111     },
24112
24113 /**
24114  * Filter by a function. The passed function will be called with each
24115  * object in the current dataset. If the function returns true the value is kept,
24116  * otherwise it is filtered.
24117  * @param {Function} fn
24118  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24119  */
24120     filterBy : function(fn, scope){
24121         if(this.jsonData){
24122             var data = [];
24123             var ss = this.snapshot;
24124             for(var i = 0, len = ss.length; i < len; i++){
24125                 var o = ss[i];
24126                 if(fn.call(scope || this, o)){
24127                     data.push(o);
24128                 }
24129             }
24130             this.jsonData = data;
24131             this.refresh();
24132         }
24133     },
24134
24135 /**
24136  * Clears the current filter.
24137  */
24138     clearFilter : function(){
24139         if(this.snapshot && this.jsonData != this.snapshot){
24140             this.jsonData = this.snapshot;
24141             this.refresh();
24142         }
24143     },
24144
24145
24146 /**
24147  * Sorts the data for this view and refreshes it.
24148  * @param {String} property A property on your JSON objects to sort on
24149  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24150  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24151  */
24152     sort : function(property, dir, sortType){
24153         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24154         if(this.jsonData){
24155             var p = property;
24156             var dsc = dir && dir.toLowerCase() == "desc";
24157             var f = function(o1, o2){
24158                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24159                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24160                 ;
24161                 if(v1 < v2){
24162                     return dsc ? +1 : -1;
24163                 } else if(v1 > v2){
24164                     return dsc ? -1 : +1;
24165                 } else{
24166                     return 0;
24167                 }
24168             };
24169             this.jsonData.sort(f);
24170             this.refresh();
24171             if(this.jsonData != this.snapshot){
24172                 this.snapshot.sort(f);
24173             }
24174         }
24175     }
24176 });/*
24177  * Based on:
24178  * Ext JS Library 1.1.1
24179  * Copyright(c) 2006-2007, Ext JS, LLC.
24180  *
24181  * Originally Released Under LGPL - original licence link has changed is not relivant.
24182  *
24183  * Fork - LGPL
24184  * <script type="text/javascript">
24185  */
24186  
24187
24188 /**
24189  * @class Roo.ColorPalette
24190  * @extends Roo.Component
24191  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24192  * Here's an example of typical usage:
24193  * <pre><code>
24194 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24195 cp.render('my-div');
24196
24197 cp.on('select', function(palette, selColor){
24198     // do something with selColor
24199 });
24200 </code></pre>
24201  * @constructor
24202  * Create a new ColorPalette
24203  * @param {Object} config The config object
24204  */
24205 Roo.ColorPalette = function(config){
24206     Roo.ColorPalette.superclass.constructor.call(this, config);
24207     this.addEvents({
24208         /**
24209              * @event select
24210              * Fires when a color is selected
24211              * @param {ColorPalette} this
24212              * @param {String} color The 6-digit color hex code (without the # symbol)
24213              */
24214         select: true
24215     });
24216
24217     if(this.handler){
24218         this.on("select", this.handler, this.scope, true);
24219     }
24220 };
24221 Roo.extend(Roo.ColorPalette, Roo.Component, {
24222     /**
24223      * @cfg {String} itemCls
24224      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24225      */
24226     itemCls : "x-color-palette",
24227     /**
24228      * @cfg {String} value
24229      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24230      * the hex codes are case-sensitive.
24231      */
24232     value : null,
24233     clickEvent:'click',
24234     // private
24235     ctype: "Roo.ColorPalette",
24236
24237     /**
24238      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24239      */
24240     allowReselect : false,
24241
24242     /**
24243      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24244      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24245      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24246      * of colors with the width setting until the box is symmetrical.</p>
24247      * <p>You can override individual colors if needed:</p>
24248      * <pre><code>
24249 var cp = new Roo.ColorPalette();
24250 cp.colors[0] = "FF0000";  // change the first box to red
24251 </code></pre>
24252
24253 Or you can provide a custom array of your own for complete control:
24254 <pre><code>
24255 var cp = new Roo.ColorPalette();
24256 cp.colors = ["000000", "993300", "333300"];
24257 </code></pre>
24258      * @type Array
24259      */
24260     colors : [
24261         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24262         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24263         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24264         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24265         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24266     ],
24267
24268     // private
24269     onRender : function(container, position){
24270         var t = new Roo.MasterTemplate(
24271             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24272         );
24273         var c = this.colors;
24274         for(var i = 0, len = c.length; i < len; i++){
24275             t.add([c[i]]);
24276         }
24277         var el = document.createElement("div");
24278         el.className = this.itemCls;
24279         t.overwrite(el);
24280         container.dom.insertBefore(el, position);
24281         this.el = Roo.get(el);
24282         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24283         if(this.clickEvent != 'click'){
24284             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24285         }
24286     },
24287
24288     // private
24289     afterRender : function(){
24290         Roo.ColorPalette.superclass.afterRender.call(this);
24291         if(this.value){
24292             var s = this.value;
24293             this.value = null;
24294             this.select(s);
24295         }
24296     },
24297
24298     // private
24299     handleClick : function(e, t){
24300         e.preventDefault();
24301         if(!this.disabled){
24302             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24303             this.select(c.toUpperCase());
24304         }
24305     },
24306
24307     /**
24308      * Selects the specified color in the palette (fires the select event)
24309      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24310      */
24311     select : function(color){
24312         color = color.replace("#", "");
24313         if(color != this.value || this.allowReselect){
24314             var el = this.el;
24315             if(this.value){
24316                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24317             }
24318             el.child("a.color-"+color).addClass("x-color-palette-sel");
24319             this.value = color;
24320             this.fireEvent("select", this, color);
24321         }
24322     }
24323 });/*
24324  * Based on:
24325  * Ext JS Library 1.1.1
24326  * Copyright(c) 2006-2007, Ext JS, LLC.
24327  *
24328  * Originally Released Under LGPL - original licence link has changed is not relivant.
24329  *
24330  * Fork - LGPL
24331  * <script type="text/javascript">
24332  */
24333  
24334 /**
24335  * @class Roo.DatePicker
24336  * @extends Roo.Component
24337  * Simple date picker class.
24338  * @constructor
24339  * Create a new DatePicker
24340  * @param {Object} config The config object
24341  */
24342 Roo.DatePicker = function(config){
24343     Roo.DatePicker.superclass.constructor.call(this, config);
24344
24345     this.value = config && config.value ?
24346                  config.value.clearTime() : new Date().clearTime();
24347
24348     this.addEvents({
24349         /**
24350              * @event select
24351              * Fires when a date is selected
24352              * @param {DatePicker} this
24353              * @param {Date} date The selected date
24354              */
24355         select: true
24356     });
24357
24358     if(this.handler){
24359         this.on("select", this.handler,  this.scope || this);
24360     }
24361     // build the disabledDatesRE
24362     if(!this.disabledDatesRE && this.disabledDates){
24363         var dd = this.disabledDates;
24364         var re = "(?:";
24365         for(var i = 0; i < dd.length; i++){
24366             re += dd[i];
24367             if(i != dd.length-1) re += "|";
24368         }
24369         this.disabledDatesRE = new RegExp(re + ")");
24370     }
24371 };
24372
24373 Roo.extend(Roo.DatePicker, Roo.Component, {
24374     /**
24375      * @cfg {String} todayText
24376      * The text to display on the button that selects the current date (defaults to "Today")
24377      */
24378     todayText : "Today",
24379     /**
24380      * @cfg {String} okText
24381      * The text to display on the ok button
24382      */
24383     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24384     /**
24385      * @cfg {String} cancelText
24386      * The text to display on the cancel button
24387      */
24388     cancelText : "Cancel",
24389     /**
24390      * @cfg {String} todayTip
24391      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24392      */
24393     todayTip : "{0} (Spacebar)",
24394     /**
24395      * @cfg {Date} minDate
24396      * Minimum allowable date (JavaScript date object, defaults to null)
24397      */
24398     minDate : null,
24399     /**
24400      * @cfg {Date} maxDate
24401      * Maximum allowable date (JavaScript date object, defaults to null)
24402      */
24403     maxDate : null,
24404     /**
24405      * @cfg {String} minText
24406      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24407      */
24408     minText : "This date is before the minimum date",
24409     /**
24410      * @cfg {String} maxText
24411      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24412      */
24413     maxText : "This date is after the maximum date",
24414     /**
24415      * @cfg {String} format
24416      * The default date format string which can be overriden for localization support.  The format must be
24417      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24418      */
24419     format : "m/d/y",
24420     /**
24421      * @cfg {Array} disabledDays
24422      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24423      */
24424     disabledDays : null,
24425     /**
24426      * @cfg {String} disabledDaysText
24427      * The tooltip to display when the date falls on a disabled day (defaults to "")
24428      */
24429     disabledDaysText : "",
24430     /**
24431      * @cfg {RegExp} disabledDatesRE
24432      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24433      */
24434     disabledDatesRE : null,
24435     /**
24436      * @cfg {String} disabledDatesText
24437      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24438      */
24439     disabledDatesText : "",
24440     /**
24441      * @cfg {Boolean} constrainToViewport
24442      * True to constrain the date picker to the viewport (defaults to true)
24443      */
24444     constrainToViewport : true,
24445     /**
24446      * @cfg {Array} monthNames
24447      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24448      */
24449     monthNames : Date.monthNames,
24450     /**
24451      * @cfg {Array} dayNames
24452      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24453      */
24454     dayNames : Date.dayNames,
24455     /**
24456      * @cfg {String} nextText
24457      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24458      */
24459     nextText: 'Next Month (Control+Right)',
24460     /**
24461      * @cfg {String} prevText
24462      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24463      */
24464     prevText: 'Previous Month (Control+Left)',
24465     /**
24466      * @cfg {String} monthYearText
24467      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24468      */
24469     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24470     /**
24471      * @cfg {Number} startDay
24472      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24473      */
24474     startDay : 0,
24475     /**
24476      * @cfg {Bool} showClear
24477      * Show a clear button (usefull for date form elements that can be blank.)
24478      */
24479     
24480     showClear: false,
24481     
24482     /**
24483      * Sets the value of the date field
24484      * @param {Date} value The date to set
24485      */
24486     setValue : function(value){
24487         var old = this.value;
24488         this.value = value.clearTime(true);
24489         if(this.el){
24490             this.update(this.value);
24491         }
24492     },
24493
24494     /**
24495      * Gets the current selected value of the date field
24496      * @return {Date} The selected date
24497      */
24498     getValue : function(){
24499         return this.value;
24500     },
24501
24502     // private
24503     focus : function(){
24504         if(this.el){
24505             this.update(this.activeDate);
24506         }
24507     },
24508
24509     // private
24510     onRender : function(container, position){
24511         var m = [
24512              '<table cellspacing="0">',
24513                 '<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>',
24514                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24515         var dn = this.dayNames;
24516         for(var i = 0; i < 7; i++){
24517             var d = this.startDay+i;
24518             if(d > 6){
24519                 d = d-7;
24520             }
24521             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24522         }
24523         m[m.length] = "</tr></thead><tbody><tr>";
24524         for(var i = 0; i < 42; i++) {
24525             if(i % 7 == 0 && i != 0){
24526                 m[m.length] = "</tr><tr>";
24527             }
24528             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24529         }
24530         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24531             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24532
24533         var el = document.createElement("div");
24534         el.className = "x-date-picker";
24535         el.innerHTML = m.join("");
24536
24537         container.dom.insertBefore(el, position);
24538
24539         this.el = Roo.get(el);
24540         this.eventEl = Roo.get(el.firstChild);
24541
24542         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24543             handler: this.showPrevMonth,
24544             scope: this,
24545             preventDefault:true,
24546             stopDefault:true
24547         });
24548
24549         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24550             handler: this.showNextMonth,
24551             scope: this,
24552             preventDefault:true,
24553             stopDefault:true
24554         });
24555
24556         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24557
24558         this.monthPicker = this.el.down('div.x-date-mp');
24559         this.monthPicker.enableDisplayMode('block');
24560         
24561         var kn = new Roo.KeyNav(this.eventEl, {
24562             "left" : function(e){
24563                 e.ctrlKey ?
24564                     this.showPrevMonth() :
24565                     this.update(this.activeDate.add("d", -1));
24566             },
24567
24568             "right" : function(e){
24569                 e.ctrlKey ?
24570                     this.showNextMonth() :
24571                     this.update(this.activeDate.add("d", 1));
24572             },
24573
24574             "up" : function(e){
24575                 e.ctrlKey ?
24576                     this.showNextYear() :
24577                     this.update(this.activeDate.add("d", -7));
24578             },
24579
24580             "down" : function(e){
24581                 e.ctrlKey ?
24582                     this.showPrevYear() :
24583                     this.update(this.activeDate.add("d", 7));
24584             },
24585
24586             "pageUp" : function(e){
24587                 this.showNextMonth();
24588             },
24589
24590             "pageDown" : function(e){
24591                 this.showPrevMonth();
24592             },
24593
24594             "enter" : function(e){
24595                 e.stopPropagation();
24596                 return true;
24597             },
24598
24599             scope : this
24600         });
24601
24602         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24603
24604         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24605
24606         this.el.unselectable();
24607         
24608         this.cells = this.el.select("table.x-date-inner tbody td");
24609         this.textNodes = this.el.query("table.x-date-inner tbody span");
24610
24611         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24612             text: "&#160;",
24613             tooltip: this.monthYearText
24614         });
24615
24616         this.mbtn.on('click', this.showMonthPicker, this);
24617         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24618
24619
24620         var today = (new Date()).dateFormat(this.format);
24621         
24622         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24623         if (this.showClear) {
24624             baseTb.add( new Roo.Toolbar.Fill());
24625         }
24626         baseTb.add({
24627             text: String.format(this.todayText, today),
24628             tooltip: String.format(this.todayTip, today),
24629             handler: this.selectToday,
24630             scope: this
24631         });
24632         
24633         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24634             
24635         //});
24636         if (this.showClear) {
24637             
24638             baseTb.add( new Roo.Toolbar.Fill());
24639             baseTb.add({
24640                 text: '&#160;',
24641                 cls: 'x-btn-icon x-btn-clear',
24642                 handler: function() {
24643                     //this.value = '';
24644                     this.fireEvent("select", this, '');
24645                 },
24646                 scope: this
24647             });
24648         }
24649         
24650         
24651         if(Roo.isIE){
24652             this.el.repaint();
24653         }
24654         this.update(this.value);
24655     },
24656
24657     createMonthPicker : function(){
24658         if(!this.monthPicker.dom.firstChild){
24659             var buf = ['<table border="0" cellspacing="0">'];
24660             for(var i = 0; i < 6; i++){
24661                 buf.push(
24662                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24663                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24664                     i == 0 ?
24665                     '<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>' :
24666                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24667                 );
24668             }
24669             buf.push(
24670                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24671                     this.okText,
24672                     '</button><button type="button" class="x-date-mp-cancel">',
24673                     this.cancelText,
24674                     '</button></td></tr>',
24675                 '</table>'
24676             );
24677             this.monthPicker.update(buf.join(''));
24678             this.monthPicker.on('click', this.onMonthClick, this);
24679             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24680
24681             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24682             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24683
24684             this.mpMonths.each(function(m, a, i){
24685                 i += 1;
24686                 if((i%2) == 0){
24687                     m.dom.xmonth = 5 + Math.round(i * .5);
24688                 }else{
24689                     m.dom.xmonth = Math.round((i-1) * .5);
24690                 }
24691             });
24692         }
24693     },
24694
24695     showMonthPicker : function(){
24696         this.createMonthPicker();
24697         var size = this.el.getSize();
24698         this.monthPicker.setSize(size);
24699         this.monthPicker.child('table').setSize(size);
24700
24701         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24702         this.updateMPMonth(this.mpSelMonth);
24703         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24704         this.updateMPYear(this.mpSelYear);
24705
24706         this.monthPicker.slideIn('t', {duration:.2});
24707     },
24708
24709     updateMPYear : function(y){
24710         this.mpyear = y;
24711         var ys = this.mpYears.elements;
24712         for(var i = 1; i <= 10; i++){
24713             var td = ys[i-1], y2;
24714             if((i%2) == 0){
24715                 y2 = y + Math.round(i * .5);
24716                 td.firstChild.innerHTML = y2;
24717                 td.xyear = y2;
24718             }else{
24719                 y2 = y - (5-Math.round(i * .5));
24720                 td.firstChild.innerHTML = y2;
24721                 td.xyear = y2;
24722             }
24723             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24724         }
24725     },
24726
24727     updateMPMonth : function(sm){
24728         this.mpMonths.each(function(m, a, i){
24729             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24730         });
24731     },
24732
24733     selectMPMonth: function(m){
24734         
24735     },
24736
24737     onMonthClick : function(e, t){
24738         e.stopEvent();
24739         var el = new Roo.Element(t), pn;
24740         if(el.is('button.x-date-mp-cancel')){
24741             this.hideMonthPicker();
24742         }
24743         else if(el.is('button.x-date-mp-ok')){
24744             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24745             this.hideMonthPicker();
24746         }
24747         else if(pn = el.up('td.x-date-mp-month', 2)){
24748             this.mpMonths.removeClass('x-date-mp-sel');
24749             pn.addClass('x-date-mp-sel');
24750             this.mpSelMonth = pn.dom.xmonth;
24751         }
24752         else if(pn = el.up('td.x-date-mp-year', 2)){
24753             this.mpYears.removeClass('x-date-mp-sel');
24754             pn.addClass('x-date-mp-sel');
24755             this.mpSelYear = pn.dom.xyear;
24756         }
24757         else if(el.is('a.x-date-mp-prev')){
24758             this.updateMPYear(this.mpyear-10);
24759         }
24760         else if(el.is('a.x-date-mp-next')){
24761             this.updateMPYear(this.mpyear+10);
24762         }
24763     },
24764
24765     onMonthDblClick : function(e, t){
24766         e.stopEvent();
24767         var el = new Roo.Element(t), pn;
24768         if(pn = el.up('td.x-date-mp-month', 2)){
24769             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24770             this.hideMonthPicker();
24771         }
24772         else if(pn = el.up('td.x-date-mp-year', 2)){
24773             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24774             this.hideMonthPicker();
24775         }
24776     },
24777
24778     hideMonthPicker : function(disableAnim){
24779         if(this.monthPicker){
24780             if(disableAnim === true){
24781                 this.monthPicker.hide();
24782             }else{
24783                 this.monthPicker.slideOut('t', {duration:.2});
24784             }
24785         }
24786     },
24787
24788     // private
24789     showPrevMonth : function(e){
24790         this.update(this.activeDate.add("mo", -1));
24791     },
24792
24793     // private
24794     showNextMonth : function(e){
24795         this.update(this.activeDate.add("mo", 1));
24796     },
24797
24798     // private
24799     showPrevYear : function(){
24800         this.update(this.activeDate.add("y", -1));
24801     },
24802
24803     // private
24804     showNextYear : function(){
24805         this.update(this.activeDate.add("y", 1));
24806     },
24807
24808     // private
24809     handleMouseWheel : function(e){
24810         var delta = e.getWheelDelta();
24811         if(delta > 0){
24812             this.showPrevMonth();
24813             e.stopEvent();
24814         } else if(delta < 0){
24815             this.showNextMonth();
24816             e.stopEvent();
24817         }
24818     },
24819
24820     // private
24821     handleDateClick : function(e, t){
24822         e.stopEvent();
24823         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24824             this.setValue(new Date(t.dateValue));
24825             this.fireEvent("select", this, this.value);
24826         }
24827     },
24828
24829     // private
24830     selectToday : function(){
24831         this.setValue(new Date().clearTime());
24832         this.fireEvent("select", this, this.value);
24833     },
24834
24835     // private
24836     update : function(date){
24837         var vd = this.activeDate;
24838         this.activeDate = date;
24839         if(vd && this.el){
24840             var t = date.getTime();
24841             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24842                 this.cells.removeClass("x-date-selected");
24843                 this.cells.each(function(c){
24844                    if(c.dom.firstChild.dateValue == t){
24845                        c.addClass("x-date-selected");
24846                        setTimeout(function(){
24847                             try{c.dom.firstChild.focus();}catch(e){}
24848                        }, 50);
24849                        return false;
24850                    }
24851                 });
24852                 return;
24853             }
24854         }
24855         var days = date.getDaysInMonth();
24856         var firstOfMonth = date.getFirstDateOfMonth();
24857         var startingPos = firstOfMonth.getDay()-this.startDay;
24858
24859         if(startingPos <= this.startDay){
24860             startingPos += 7;
24861         }
24862
24863         var pm = date.add("mo", -1);
24864         var prevStart = pm.getDaysInMonth()-startingPos;
24865
24866         var cells = this.cells.elements;
24867         var textEls = this.textNodes;
24868         days += startingPos;
24869
24870         // convert everything to numbers so it's fast
24871         var day = 86400000;
24872         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24873         var today = new Date().clearTime().getTime();
24874         var sel = date.clearTime().getTime();
24875         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24876         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24877         var ddMatch = this.disabledDatesRE;
24878         var ddText = this.disabledDatesText;
24879         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24880         var ddaysText = this.disabledDaysText;
24881         var format = this.format;
24882
24883         var setCellClass = function(cal, cell){
24884             cell.title = "";
24885             var t = d.getTime();
24886             cell.firstChild.dateValue = t;
24887             if(t == today){
24888                 cell.className += " x-date-today";
24889                 cell.title = cal.todayText;
24890             }
24891             if(t == sel){
24892                 cell.className += " x-date-selected";
24893                 setTimeout(function(){
24894                     try{cell.firstChild.focus();}catch(e){}
24895                 }, 50);
24896             }
24897             // disabling
24898             if(t < min) {
24899                 cell.className = " x-date-disabled";
24900                 cell.title = cal.minText;
24901                 return;
24902             }
24903             if(t > max) {
24904                 cell.className = " x-date-disabled";
24905                 cell.title = cal.maxText;
24906                 return;
24907             }
24908             if(ddays){
24909                 if(ddays.indexOf(d.getDay()) != -1){
24910                     cell.title = ddaysText;
24911                     cell.className = " x-date-disabled";
24912                 }
24913             }
24914             if(ddMatch && format){
24915                 var fvalue = d.dateFormat(format);
24916                 if(ddMatch.test(fvalue)){
24917                     cell.title = ddText.replace("%0", fvalue);
24918                     cell.className = " x-date-disabled";
24919                 }
24920             }
24921         };
24922
24923         var i = 0;
24924         for(; i < startingPos; i++) {
24925             textEls[i].innerHTML = (++prevStart);
24926             d.setDate(d.getDate()+1);
24927             cells[i].className = "x-date-prevday";
24928             setCellClass(this, cells[i]);
24929         }
24930         for(; i < days; i++){
24931             intDay = i - startingPos + 1;
24932             textEls[i].innerHTML = (intDay);
24933             d.setDate(d.getDate()+1);
24934             cells[i].className = "x-date-active";
24935             setCellClass(this, cells[i]);
24936         }
24937         var extraDays = 0;
24938         for(; i < 42; i++) {
24939              textEls[i].innerHTML = (++extraDays);
24940              d.setDate(d.getDate()+1);
24941              cells[i].className = "x-date-nextday";
24942              setCellClass(this, cells[i]);
24943         }
24944
24945         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24946
24947         if(!this.internalRender){
24948             var main = this.el.dom.firstChild;
24949             var w = main.offsetWidth;
24950             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24951             Roo.fly(main).setWidth(w);
24952             this.internalRender = true;
24953             // opera does not respect the auto grow header center column
24954             // then, after it gets a width opera refuses to recalculate
24955             // without a second pass
24956             if(Roo.isOpera && !this.secondPass){
24957                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24958                 this.secondPass = true;
24959                 this.update.defer(10, this, [date]);
24960             }
24961         }
24962     }
24963 });/*
24964  * Based on:
24965  * Ext JS Library 1.1.1
24966  * Copyright(c) 2006-2007, Ext JS, LLC.
24967  *
24968  * Originally Released Under LGPL - original licence link has changed is not relivant.
24969  *
24970  * Fork - LGPL
24971  * <script type="text/javascript">
24972  */
24973 /**
24974  * @class Roo.TabPanel
24975  * @extends Roo.util.Observable
24976  * A lightweight tab container.
24977  * <br><br>
24978  * Usage:
24979  * <pre><code>
24980 // basic tabs 1, built from existing content
24981 var tabs = new Roo.TabPanel("tabs1");
24982 tabs.addTab("script", "View Script");
24983 tabs.addTab("markup", "View Markup");
24984 tabs.activate("script");
24985
24986 // more advanced tabs, built from javascript
24987 var jtabs = new Roo.TabPanel("jtabs");
24988 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24989
24990 // set up the UpdateManager
24991 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24992 var updater = tab2.getUpdateManager();
24993 updater.setDefaultUrl("ajax1.htm");
24994 tab2.on('activate', updater.refresh, updater, true);
24995
24996 // Use setUrl for Ajax loading
24997 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24998 tab3.setUrl("ajax2.htm", null, true);
24999
25000 // Disabled tab
25001 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25002 tab4.disable();
25003
25004 jtabs.activate("jtabs-1");
25005  * </code></pre>
25006  * @constructor
25007  * Create a new TabPanel.
25008  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25009  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25010  */
25011 Roo.TabPanel = function(container, config){
25012     /**
25013     * The container element for this TabPanel.
25014     * @type Roo.Element
25015     */
25016     this.el = Roo.get(container, true);
25017     if(config){
25018         if(typeof config == "boolean"){
25019             this.tabPosition = config ? "bottom" : "top";
25020         }else{
25021             Roo.apply(this, config);
25022         }
25023     }
25024     if(this.tabPosition == "bottom"){
25025         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25026         this.el.addClass("x-tabs-bottom");
25027     }
25028     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25029     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25030     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25031     if(Roo.isIE){
25032         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25033     }
25034     if(this.tabPosition != "bottom"){
25035     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25036      * @type Roo.Element
25037      */
25038       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25039       this.el.addClass("x-tabs-top");
25040     }
25041     this.items = [];
25042
25043     this.bodyEl.setStyle("position", "relative");
25044
25045     this.active = null;
25046     this.activateDelegate = this.activate.createDelegate(this);
25047
25048     this.addEvents({
25049         /**
25050          * @event tabchange
25051          * Fires when the active tab changes
25052          * @param {Roo.TabPanel} this
25053          * @param {Roo.TabPanelItem} activePanel The new active tab
25054          */
25055         "tabchange": true,
25056         /**
25057          * @event beforetabchange
25058          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25059          * @param {Roo.TabPanel} this
25060          * @param {Object} e Set cancel to true on this object to cancel the tab change
25061          * @param {Roo.TabPanelItem} tab The tab being changed to
25062          */
25063         "beforetabchange" : true
25064     });
25065
25066     Roo.EventManager.onWindowResize(this.onResize, this);
25067     this.cpad = this.el.getPadding("lr");
25068     this.hiddenCount = 0;
25069
25070     Roo.TabPanel.superclass.constructor.call(this);
25071 };
25072
25073 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25074         /*
25075          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25076          */
25077     tabPosition : "top",
25078         /*
25079          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25080          */
25081     currentTabWidth : 0,
25082         /*
25083          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25084          */
25085     minTabWidth : 40,
25086         /*
25087          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25088          */
25089     maxTabWidth : 250,
25090         /*
25091          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25092          */
25093     preferredTabWidth : 175,
25094         /*
25095          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25096          */
25097     resizeTabs : false,
25098         /*
25099          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25100          */
25101     monitorResize : true,
25102
25103     /**
25104      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25105      * @param {String} id The id of the div to use <b>or create</b>
25106      * @param {String} text The text for the tab
25107      * @param {String} content (optional) Content to put in the TabPanelItem body
25108      * @param {Boolean} closable (optional) True to create a close icon on the tab
25109      * @return {Roo.TabPanelItem} The created TabPanelItem
25110      */
25111     addTab : function(id, text, content, closable){
25112         var item = new Roo.TabPanelItem(this, id, text, closable);
25113         this.addTabItem(item);
25114         if(content){
25115             item.setContent(content);
25116         }
25117         return item;
25118     },
25119
25120     /**
25121      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25122      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25123      * @return {Roo.TabPanelItem}
25124      */
25125     getTab : function(id){
25126         return this.items[id];
25127     },
25128
25129     /**
25130      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25131      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25132      */
25133     hideTab : function(id){
25134         var t = this.items[id];
25135         if(!t.isHidden()){
25136            t.setHidden(true);
25137            this.hiddenCount++;
25138            this.autoSizeTabs();
25139         }
25140     },
25141
25142     /**
25143      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25144      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25145      */
25146     unhideTab : function(id){
25147         var t = this.items[id];
25148         if(t.isHidden()){
25149            t.setHidden(false);
25150            this.hiddenCount--;
25151            this.autoSizeTabs();
25152         }
25153     },
25154
25155     /**
25156      * Adds an existing {@link Roo.TabPanelItem}.
25157      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25158      */
25159     addTabItem : function(item){
25160         this.items[item.id] = item;
25161         this.items.push(item);
25162         if(this.resizeTabs){
25163            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25164            this.autoSizeTabs();
25165         }else{
25166             item.autoSize();
25167         }
25168     },
25169
25170     /**
25171      * Removes a {@link Roo.TabPanelItem}.
25172      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25173      */
25174     removeTab : function(id){
25175         var items = this.items;
25176         var tab = items[id];
25177         if(!tab) { return; }
25178         var index = items.indexOf(tab);
25179         if(this.active == tab && items.length > 1){
25180             var newTab = this.getNextAvailable(index);
25181             if(newTab) {
25182                 newTab.activate();
25183             }
25184         }
25185         this.stripEl.dom.removeChild(tab.pnode.dom);
25186         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25187             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25188         }
25189         items.splice(index, 1);
25190         delete this.items[tab.id];
25191         tab.fireEvent("close", tab);
25192         tab.purgeListeners();
25193         this.autoSizeTabs();
25194     },
25195
25196     getNextAvailable : function(start){
25197         var items = this.items;
25198         var index = start;
25199         // look for a next tab that will slide over to
25200         // replace the one being removed
25201         while(index < items.length){
25202             var item = items[++index];
25203             if(item && !item.isHidden()){
25204                 return item;
25205             }
25206         }
25207         // if one isn't found select the previous tab (on the left)
25208         index = start;
25209         while(index >= 0){
25210             var item = items[--index];
25211             if(item && !item.isHidden()){
25212                 return item;
25213             }
25214         }
25215         return null;
25216     },
25217
25218     /**
25219      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25220      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25221      */
25222     disableTab : function(id){
25223         var tab = this.items[id];
25224         if(tab && this.active != tab){
25225             tab.disable();
25226         }
25227     },
25228
25229     /**
25230      * Enables a {@link Roo.TabPanelItem} that is disabled.
25231      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25232      */
25233     enableTab : function(id){
25234         var tab = this.items[id];
25235         tab.enable();
25236     },
25237
25238     /**
25239      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25240      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25241      * @return {Roo.TabPanelItem} The TabPanelItem.
25242      */
25243     activate : function(id){
25244         var tab = this.items[id];
25245         if(!tab){
25246             return null;
25247         }
25248         if(tab == this.active || tab.disabled){
25249             return tab;
25250         }
25251         var e = {};
25252         this.fireEvent("beforetabchange", this, e, tab);
25253         if(e.cancel !== true && !tab.disabled){
25254             if(this.active){
25255                 this.active.hide();
25256             }
25257             this.active = this.items[id];
25258             this.active.show();
25259             this.fireEvent("tabchange", this, this.active);
25260         }
25261         return tab;
25262     },
25263
25264     /**
25265      * Gets the active {@link Roo.TabPanelItem}.
25266      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25267      */
25268     getActiveTab : function(){
25269         return this.active;
25270     },
25271
25272     /**
25273      * Updates the tab body element to fit the height of the container element
25274      * for overflow scrolling
25275      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25276      */
25277     syncHeight : function(targetHeight){
25278         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25279         var bm = this.bodyEl.getMargins();
25280         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25281         this.bodyEl.setHeight(newHeight);
25282         return newHeight;
25283     },
25284
25285     onResize : function(){
25286         if(this.monitorResize){
25287             this.autoSizeTabs();
25288         }
25289     },
25290
25291     /**
25292      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25293      */
25294     beginUpdate : function(){
25295         this.updating = true;
25296     },
25297
25298     /**
25299      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25300      */
25301     endUpdate : function(){
25302         this.updating = false;
25303         this.autoSizeTabs();
25304     },
25305
25306     /**
25307      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25308      */
25309     autoSizeTabs : function(){
25310         var count = this.items.length;
25311         var vcount = count - this.hiddenCount;
25312         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25313         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25314         var availWidth = Math.floor(w / vcount);
25315         var b = this.stripBody;
25316         if(b.getWidth() > w){
25317             var tabs = this.items;
25318             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25319             if(availWidth < this.minTabWidth){
25320                 /*if(!this.sleft){    // incomplete scrolling code
25321                     this.createScrollButtons();
25322                 }
25323                 this.showScroll();
25324                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25325             }
25326         }else{
25327             if(this.currentTabWidth < this.preferredTabWidth){
25328                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25329             }
25330         }
25331     },
25332
25333     /**
25334      * Returns the number of tabs in this TabPanel.
25335      * @return {Number}
25336      */
25337      getCount : function(){
25338          return this.items.length;
25339      },
25340
25341     /**
25342      * Resizes all the tabs to the passed width
25343      * @param {Number} The new width
25344      */
25345     setTabWidth : function(width){
25346         this.currentTabWidth = width;
25347         for(var i = 0, len = this.items.length; i < len; i++) {
25348                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25349         }
25350     },
25351
25352     /**
25353      * Destroys this TabPanel
25354      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25355      */
25356     destroy : function(removeEl){
25357         Roo.EventManager.removeResizeListener(this.onResize, this);
25358         for(var i = 0, len = this.items.length; i < len; i++){
25359             this.items[i].purgeListeners();
25360         }
25361         if(removeEl === true){
25362             this.el.update("");
25363             this.el.remove();
25364         }
25365     }
25366 });
25367
25368 /**
25369  * @class Roo.TabPanelItem
25370  * @extends Roo.util.Observable
25371  * Represents an individual item (tab plus body) in a TabPanel.
25372  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25373  * @param {String} id The id of this TabPanelItem
25374  * @param {String} text The text for the tab of this TabPanelItem
25375  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25376  */
25377 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25378     /**
25379      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25380      * @type Roo.TabPanel
25381      */
25382     this.tabPanel = tabPanel;
25383     /**
25384      * The id for this TabPanelItem
25385      * @type String
25386      */
25387     this.id = id;
25388     /** @private */
25389     this.disabled = false;
25390     /** @private */
25391     this.text = text;
25392     /** @private */
25393     this.loaded = false;
25394     this.closable = closable;
25395
25396     /**
25397      * The body element for this TabPanelItem.
25398      * @type Roo.Element
25399      */
25400     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25401     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25402     this.bodyEl.setStyle("display", "block");
25403     this.bodyEl.setStyle("zoom", "1");
25404     this.hideAction();
25405
25406     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25407     /** @private */
25408     this.el = Roo.get(els.el, true);
25409     this.inner = Roo.get(els.inner, true);
25410     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25411     this.pnode = Roo.get(els.el.parentNode, true);
25412     this.el.on("mousedown", this.onTabMouseDown, this);
25413     this.el.on("click", this.onTabClick, this);
25414     /** @private */
25415     if(closable){
25416         var c = Roo.get(els.close, true);
25417         c.dom.title = this.closeText;
25418         c.addClassOnOver("close-over");
25419         c.on("click", this.closeClick, this);
25420      }
25421
25422     this.addEvents({
25423          /**
25424          * @event activate
25425          * Fires when this tab becomes the active tab.
25426          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25427          * @param {Roo.TabPanelItem} this
25428          */
25429         "activate": true,
25430         /**
25431          * @event beforeclose
25432          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25433          * @param {Roo.TabPanelItem} this
25434          * @param {Object} e Set cancel to true on this object to cancel the close.
25435          */
25436         "beforeclose": true,
25437         /**
25438          * @event close
25439          * Fires when this tab is closed.
25440          * @param {Roo.TabPanelItem} this
25441          */
25442          "close": true,
25443         /**
25444          * @event deactivate
25445          * Fires when this tab is no longer the active tab.
25446          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25447          * @param {Roo.TabPanelItem} this
25448          */
25449          "deactivate" : true
25450     });
25451     this.hidden = false;
25452
25453     Roo.TabPanelItem.superclass.constructor.call(this);
25454 };
25455
25456 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25457     purgeListeners : function(){
25458        Roo.util.Observable.prototype.purgeListeners.call(this);
25459        this.el.removeAllListeners();
25460     },
25461     /**
25462      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25463      */
25464     show : function(){
25465         this.pnode.addClass("on");
25466         this.showAction();
25467         if(Roo.isOpera){
25468             this.tabPanel.stripWrap.repaint();
25469         }
25470         this.fireEvent("activate", this.tabPanel, this);
25471     },
25472
25473     /**
25474      * Returns true if this tab is the active tab.
25475      * @return {Boolean}
25476      */
25477     isActive : function(){
25478         return this.tabPanel.getActiveTab() == this;
25479     },
25480
25481     /**
25482      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25483      */
25484     hide : function(){
25485         this.pnode.removeClass("on");
25486         this.hideAction();
25487         this.fireEvent("deactivate", this.tabPanel, this);
25488     },
25489
25490     hideAction : function(){
25491         this.bodyEl.hide();
25492         this.bodyEl.setStyle("position", "absolute");
25493         this.bodyEl.setLeft("-20000px");
25494         this.bodyEl.setTop("-20000px");
25495     },
25496
25497     showAction : function(){
25498         this.bodyEl.setStyle("position", "relative");
25499         this.bodyEl.setTop("");
25500         this.bodyEl.setLeft("");
25501         this.bodyEl.show();
25502     },
25503
25504     /**
25505      * Set the tooltip for the tab.
25506      * @param {String} tooltip The tab's tooltip
25507      */
25508     setTooltip : function(text){
25509         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25510             this.textEl.dom.qtip = text;
25511             this.textEl.dom.removeAttribute('title');
25512         }else{
25513             this.textEl.dom.title = text;
25514         }
25515     },
25516
25517     onTabClick : function(e){
25518         e.preventDefault();
25519         this.tabPanel.activate(this.id);
25520     },
25521
25522     onTabMouseDown : function(e){
25523         e.preventDefault();
25524         this.tabPanel.activate(this.id);
25525     },
25526
25527     getWidth : function(){
25528         return this.inner.getWidth();
25529     },
25530
25531     setWidth : function(width){
25532         var iwidth = width - this.pnode.getPadding("lr");
25533         this.inner.setWidth(iwidth);
25534         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25535         this.pnode.setWidth(width);
25536     },
25537
25538     /**
25539      * Show or hide the tab
25540      * @param {Boolean} hidden True to hide or false to show.
25541      */
25542     setHidden : function(hidden){
25543         this.hidden = hidden;
25544         this.pnode.setStyle("display", hidden ? "none" : "");
25545     },
25546
25547     /**
25548      * Returns true if this tab is "hidden"
25549      * @return {Boolean}
25550      */
25551     isHidden : function(){
25552         return this.hidden;
25553     },
25554
25555     /**
25556      * Returns the text for this tab
25557      * @return {String}
25558      */
25559     getText : function(){
25560         return this.text;
25561     },
25562
25563     autoSize : function(){
25564         //this.el.beginMeasure();
25565         this.textEl.setWidth(1);
25566         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25567         //this.el.endMeasure();
25568     },
25569
25570     /**
25571      * Sets the text for the tab (Note: this also sets the tooltip text)
25572      * @param {String} text The tab's text and tooltip
25573      */
25574     setText : function(text){
25575         this.text = text;
25576         this.textEl.update(text);
25577         this.setTooltip(text);
25578         if(!this.tabPanel.resizeTabs){
25579             this.autoSize();
25580         }
25581     },
25582     /**
25583      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25584      */
25585     activate : function(){
25586         this.tabPanel.activate(this.id);
25587     },
25588
25589     /**
25590      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25591      */
25592     disable : function(){
25593         if(this.tabPanel.active != this){
25594             this.disabled = true;
25595             this.pnode.addClass("disabled");
25596         }
25597     },
25598
25599     /**
25600      * Enables this TabPanelItem if it was previously disabled.
25601      */
25602     enable : function(){
25603         this.disabled = false;
25604         this.pnode.removeClass("disabled");
25605     },
25606
25607     /**
25608      * Sets the content for this TabPanelItem.
25609      * @param {String} content The content
25610      * @param {Boolean} loadScripts true to look for and load scripts
25611      */
25612     setContent : function(content, loadScripts){
25613         this.bodyEl.update(content, loadScripts);
25614     },
25615
25616     /**
25617      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25618      * @return {Roo.UpdateManager} The UpdateManager
25619      */
25620     getUpdateManager : function(){
25621         return this.bodyEl.getUpdateManager();
25622     },
25623
25624     /**
25625      * Set a URL to be used to load the content for this TabPanelItem.
25626      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25627      * @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)
25628      * @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)
25629      * @return {Roo.UpdateManager} The UpdateManager
25630      */
25631     setUrl : function(url, params, loadOnce){
25632         if(this.refreshDelegate){
25633             this.un('activate', this.refreshDelegate);
25634         }
25635         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25636         this.on("activate", this.refreshDelegate);
25637         return this.bodyEl.getUpdateManager();
25638     },
25639
25640     /** @private */
25641     _handleRefresh : function(url, params, loadOnce){
25642         if(!loadOnce || !this.loaded){
25643             var updater = this.bodyEl.getUpdateManager();
25644             updater.update(url, params, this._setLoaded.createDelegate(this));
25645         }
25646     },
25647
25648     /**
25649      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25650      *   Will fail silently if the setUrl method has not been called.
25651      *   This does not activate the panel, just updates its content.
25652      */
25653     refresh : function(){
25654         if(this.refreshDelegate){
25655            this.loaded = false;
25656            this.refreshDelegate();
25657         }
25658     },
25659
25660     /** @private */
25661     _setLoaded : function(){
25662         this.loaded = true;
25663     },
25664
25665     /** @private */
25666     closeClick : function(e){
25667         var o = {};
25668         e.stopEvent();
25669         this.fireEvent("beforeclose", this, o);
25670         if(o.cancel !== true){
25671             this.tabPanel.removeTab(this.id);
25672         }
25673     },
25674     /**
25675      * The text displayed in the tooltip for the close icon.
25676      * @type String
25677      */
25678     closeText : "Close this tab"
25679 });
25680
25681 /** @private */
25682 Roo.TabPanel.prototype.createStrip = function(container){
25683     var strip = document.createElement("div");
25684     strip.className = "x-tabs-wrap";
25685     container.appendChild(strip);
25686     return strip;
25687 };
25688 /** @private */
25689 Roo.TabPanel.prototype.createStripList = function(strip){
25690     // div wrapper for retard IE
25691     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
25692     return strip.firstChild.firstChild.firstChild.firstChild;
25693 };
25694 /** @private */
25695 Roo.TabPanel.prototype.createBody = function(container){
25696     var body = document.createElement("div");
25697     Roo.id(body, "tab-body");
25698     Roo.fly(body).addClass("x-tabs-body");
25699     container.appendChild(body);
25700     return body;
25701 };
25702 /** @private */
25703 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25704     var body = Roo.getDom(id);
25705     if(!body){
25706         body = document.createElement("div");
25707         body.id = id;
25708     }
25709     Roo.fly(body).addClass("x-tabs-item-body");
25710     bodyEl.insertBefore(body, bodyEl.firstChild);
25711     return body;
25712 };
25713 /** @private */
25714 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25715     var td = document.createElement("td");
25716     stripEl.appendChild(td);
25717     if(closable){
25718         td.className = "x-tabs-closable";
25719         if(!this.closeTpl){
25720             this.closeTpl = new Roo.Template(
25721                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25722                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25723                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25724             );
25725         }
25726         var el = this.closeTpl.overwrite(td, {"text": text});
25727         var close = el.getElementsByTagName("div")[0];
25728         var inner = el.getElementsByTagName("em")[0];
25729         return {"el": el, "close": close, "inner": inner};
25730     } else {
25731         if(!this.tabTpl){
25732             this.tabTpl = new Roo.Template(
25733                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25734                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25735             );
25736         }
25737         var el = this.tabTpl.overwrite(td, {"text": text});
25738         var inner = el.getElementsByTagName("em")[0];
25739         return {"el": el, "inner": inner};
25740     }
25741 };/*
25742  * Based on:
25743  * Ext JS Library 1.1.1
25744  * Copyright(c) 2006-2007, Ext JS, LLC.
25745  *
25746  * Originally Released Under LGPL - original licence link has changed is not relivant.
25747  *
25748  * Fork - LGPL
25749  * <script type="text/javascript">
25750  */
25751
25752 /**
25753  * @class Roo.Button
25754  * @extends Roo.util.Observable
25755  * Simple Button class
25756  * @cfg {String} text The button text
25757  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25758  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25759  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25760  * @cfg {Object} scope The scope of the handler
25761  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25762  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25763  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25764  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25765  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25766  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25767    applies if enableToggle = true)
25768  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25769  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25770   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25771  * @constructor
25772  * Create a new button
25773  * @param {Object} config The config object
25774  */
25775 Roo.Button = function(renderTo, config)
25776 {
25777     if (!config) {
25778         config = renderTo;
25779         renderTo = config.renderTo || false;
25780     }
25781     
25782     Roo.apply(this, config);
25783     this.addEvents({
25784         /**
25785              * @event click
25786              * Fires when this button is clicked
25787              * @param {Button} this
25788              * @param {EventObject} e The click event
25789              */
25790             "click" : true,
25791         /**
25792              * @event toggle
25793              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25794              * @param {Button} this
25795              * @param {Boolean} pressed
25796              */
25797             "toggle" : true,
25798         /**
25799              * @event mouseover
25800              * Fires when the mouse hovers over the button
25801              * @param {Button} this
25802              * @param {Event} e The event object
25803              */
25804         'mouseover' : true,
25805         /**
25806              * @event mouseout
25807              * Fires when the mouse exits the button
25808              * @param {Button} this
25809              * @param {Event} e The event object
25810              */
25811         'mouseout': true,
25812          /**
25813              * @event render
25814              * Fires when the button is rendered
25815              * @param {Button} this
25816              */
25817         'render': true
25818     });
25819     if(this.menu){
25820         this.menu = Roo.menu.MenuMgr.get(this.menu);
25821     }
25822     // register listeners first!!  - so render can be captured..
25823     Roo.util.Observable.call(this);
25824     if(renderTo){
25825         this.render(renderTo);
25826     }
25827     
25828   
25829 };
25830
25831 Roo.extend(Roo.Button, Roo.util.Observable, {
25832     /**
25833      * 
25834      */
25835     
25836     /**
25837      * Read-only. True if this button is hidden
25838      * @type Boolean
25839      */
25840     hidden : false,
25841     /**
25842      * Read-only. True if this button is disabled
25843      * @type Boolean
25844      */
25845     disabled : false,
25846     /**
25847      * Read-only. True if this button is pressed (only if enableToggle = true)
25848      * @type Boolean
25849      */
25850     pressed : false,
25851
25852     /**
25853      * @cfg {Number} tabIndex 
25854      * The DOM tabIndex for this button (defaults to undefined)
25855      */
25856     tabIndex : undefined,
25857
25858     /**
25859      * @cfg {Boolean} enableToggle
25860      * True to enable pressed/not pressed toggling (defaults to false)
25861      */
25862     enableToggle: false,
25863     /**
25864      * @cfg {Mixed} menu
25865      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25866      */
25867     menu : undefined,
25868     /**
25869      * @cfg {String} menuAlign
25870      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25871      */
25872     menuAlign : "tl-bl?",
25873
25874     /**
25875      * @cfg {String} iconCls
25876      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25877      */
25878     iconCls : undefined,
25879     /**
25880      * @cfg {String} type
25881      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25882      */
25883     type : 'button',
25884
25885     // private
25886     menuClassTarget: 'tr',
25887
25888     /**
25889      * @cfg {String} clickEvent
25890      * The type of event to map to the button's event handler (defaults to 'click')
25891      */
25892     clickEvent : 'click',
25893
25894     /**
25895      * @cfg {Boolean} handleMouseEvents
25896      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25897      */
25898     handleMouseEvents : true,
25899
25900     /**
25901      * @cfg {String} tooltipType
25902      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25903      */
25904     tooltipType : 'qtip',
25905
25906     /**
25907      * @cfg {String} cls
25908      * A CSS class to apply to the button's main element.
25909      */
25910     
25911     /**
25912      * @cfg {Roo.Template} template (Optional)
25913      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25914      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25915      * require code modifications if required elements (e.g. a button) aren't present.
25916      */
25917
25918     // private
25919     render : function(renderTo){
25920         var btn;
25921         if(this.hideParent){
25922             this.parentEl = Roo.get(renderTo);
25923         }
25924         if(!this.dhconfig){
25925             if(!this.template){
25926                 if(!Roo.Button.buttonTemplate){
25927                     // hideous table template
25928                     Roo.Button.buttonTemplate = new Roo.Template(
25929                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25930                         '<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>',
25931                         "</tr></tbody></table>");
25932                 }
25933                 this.template = Roo.Button.buttonTemplate;
25934             }
25935             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25936             var btnEl = btn.child("button:first");
25937             btnEl.on('focus', this.onFocus, this);
25938             btnEl.on('blur', this.onBlur, this);
25939             if(this.cls){
25940                 btn.addClass(this.cls);
25941             }
25942             if(this.icon){
25943                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25944             }
25945             if(this.iconCls){
25946                 btnEl.addClass(this.iconCls);
25947                 if(!this.cls){
25948                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25949                 }
25950             }
25951             if(this.tabIndex !== undefined){
25952                 btnEl.dom.tabIndex = this.tabIndex;
25953             }
25954             if(this.tooltip){
25955                 if(typeof this.tooltip == 'object'){
25956                     Roo.QuickTips.tips(Roo.apply({
25957                           target: btnEl.id
25958                     }, this.tooltip));
25959                 } else {
25960                     btnEl.dom[this.tooltipType] = this.tooltip;
25961                 }
25962             }
25963         }else{
25964             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25965         }
25966         this.el = btn;
25967         if(this.id){
25968             this.el.dom.id = this.el.id = this.id;
25969         }
25970         if(this.menu){
25971             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25972             this.menu.on("show", this.onMenuShow, this);
25973             this.menu.on("hide", this.onMenuHide, this);
25974         }
25975         btn.addClass("x-btn");
25976         if(Roo.isIE && !Roo.isIE7){
25977             this.autoWidth.defer(1, this);
25978         }else{
25979             this.autoWidth();
25980         }
25981         if(this.handleMouseEvents){
25982             btn.on("mouseover", this.onMouseOver, this);
25983             btn.on("mouseout", this.onMouseOut, this);
25984             btn.on("mousedown", this.onMouseDown, this);
25985         }
25986         btn.on(this.clickEvent, this.onClick, this);
25987         //btn.on("mouseup", this.onMouseUp, this);
25988         if(this.hidden){
25989             this.hide();
25990         }
25991         if(this.disabled){
25992             this.disable();
25993         }
25994         Roo.ButtonToggleMgr.register(this);
25995         if(this.pressed){
25996             this.el.addClass("x-btn-pressed");
25997         }
25998         if(this.repeat){
25999             var repeater = new Roo.util.ClickRepeater(btn,
26000                 typeof this.repeat == "object" ? this.repeat : {}
26001             );
26002             repeater.on("click", this.onClick,  this);
26003         }
26004         
26005         this.fireEvent('render', this);
26006         
26007     },
26008     /**
26009      * Returns the button's underlying element
26010      * @return {Roo.Element} The element
26011      */
26012     getEl : function(){
26013         return this.el;  
26014     },
26015     
26016     /**
26017      * Destroys this Button and removes any listeners.
26018      */
26019     destroy : function(){
26020         Roo.ButtonToggleMgr.unregister(this);
26021         this.el.removeAllListeners();
26022         this.purgeListeners();
26023         this.el.remove();
26024     },
26025
26026     // private
26027     autoWidth : function(){
26028         if(this.el){
26029             this.el.setWidth("auto");
26030             if(Roo.isIE7 && Roo.isStrict){
26031                 var ib = this.el.child('button');
26032                 if(ib && ib.getWidth() > 20){
26033                     ib.clip();
26034                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26035                 }
26036             }
26037             if(this.minWidth){
26038                 if(this.hidden){
26039                     this.el.beginMeasure();
26040                 }
26041                 if(this.el.getWidth() < this.minWidth){
26042                     this.el.setWidth(this.minWidth);
26043                 }
26044                 if(this.hidden){
26045                     this.el.endMeasure();
26046                 }
26047             }
26048         }
26049     },
26050
26051     /**
26052      * Assigns this button's click handler
26053      * @param {Function} handler The function to call when the button is clicked
26054      * @param {Object} scope (optional) Scope for the function passed in
26055      */
26056     setHandler : function(handler, scope){
26057         this.handler = handler;
26058         this.scope = scope;  
26059     },
26060     
26061     /**
26062      * Sets this button's text
26063      * @param {String} text The button text
26064      */
26065     setText : function(text){
26066         this.text = text;
26067         if(this.el){
26068             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26069         }
26070         this.autoWidth();
26071     },
26072     
26073     /**
26074      * Gets the text for this button
26075      * @return {String} The button text
26076      */
26077     getText : function(){
26078         return this.text;  
26079     },
26080     
26081     /**
26082      * Show this button
26083      */
26084     show: function(){
26085         this.hidden = false;
26086         if(this.el){
26087             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26088         }
26089     },
26090     
26091     /**
26092      * Hide this button
26093      */
26094     hide: function(){
26095         this.hidden = true;
26096         if(this.el){
26097             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26098         }
26099     },
26100     
26101     /**
26102      * Convenience function for boolean show/hide
26103      * @param {Boolean} visible True to show, false to hide
26104      */
26105     setVisible: function(visible){
26106         if(visible) {
26107             this.show();
26108         }else{
26109             this.hide();
26110         }
26111     },
26112     
26113     /**
26114      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26115      * @param {Boolean} state (optional) Force a particular state
26116      */
26117     toggle : function(state){
26118         state = state === undefined ? !this.pressed : state;
26119         if(state != this.pressed){
26120             if(state){
26121                 this.el.addClass("x-btn-pressed");
26122                 this.pressed = true;
26123                 this.fireEvent("toggle", this, true);
26124             }else{
26125                 this.el.removeClass("x-btn-pressed");
26126                 this.pressed = false;
26127                 this.fireEvent("toggle", this, false);
26128             }
26129             if(this.toggleHandler){
26130                 this.toggleHandler.call(this.scope || this, this, state);
26131             }
26132         }
26133     },
26134     
26135     /**
26136      * Focus the button
26137      */
26138     focus : function(){
26139         this.el.child('button:first').focus();
26140     },
26141     
26142     /**
26143      * Disable this button
26144      */
26145     disable : function(){
26146         if(this.el){
26147             this.el.addClass("x-btn-disabled");
26148         }
26149         this.disabled = true;
26150     },
26151     
26152     /**
26153      * Enable this button
26154      */
26155     enable : function(){
26156         if(this.el){
26157             this.el.removeClass("x-btn-disabled");
26158         }
26159         this.disabled = false;
26160     },
26161
26162     /**
26163      * Convenience function for boolean enable/disable
26164      * @param {Boolean} enabled True to enable, false to disable
26165      */
26166     setDisabled : function(v){
26167         this[v !== true ? "enable" : "disable"]();
26168     },
26169
26170     // private
26171     onClick : function(e){
26172         if(e){
26173             e.preventDefault();
26174         }
26175         if(e.button != 0){
26176             return;
26177         }
26178         if(!this.disabled){
26179             if(this.enableToggle){
26180                 this.toggle();
26181             }
26182             if(this.menu && !this.menu.isVisible()){
26183                 this.menu.show(this.el, this.menuAlign);
26184             }
26185             this.fireEvent("click", this, e);
26186             if(this.handler){
26187                 this.el.removeClass("x-btn-over");
26188                 this.handler.call(this.scope || this, this, e);
26189             }
26190         }
26191     },
26192     // private
26193     onMouseOver : function(e){
26194         if(!this.disabled){
26195             this.el.addClass("x-btn-over");
26196             this.fireEvent('mouseover', this, e);
26197         }
26198     },
26199     // private
26200     onMouseOut : function(e){
26201         if(!e.within(this.el,  true)){
26202             this.el.removeClass("x-btn-over");
26203             this.fireEvent('mouseout', this, e);
26204         }
26205     },
26206     // private
26207     onFocus : function(e){
26208         if(!this.disabled){
26209             this.el.addClass("x-btn-focus");
26210         }
26211     },
26212     // private
26213     onBlur : function(e){
26214         this.el.removeClass("x-btn-focus");
26215     },
26216     // private
26217     onMouseDown : function(e){
26218         if(!this.disabled && e.button == 0){
26219             this.el.addClass("x-btn-click");
26220             Roo.get(document).on('mouseup', this.onMouseUp, this);
26221         }
26222     },
26223     // private
26224     onMouseUp : function(e){
26225         if(e.button == 0){
26226             this.el.removeClass("x-btn-click");
26227             Roo.get(document).un('mouseup', this.onMouseUp, this);
26228         }
26229     },
26230     // private
26231     onMenuShow : function(e){
26232         this.el.addClass("x-btn-menu-active");
26233     },
26234     // private
26235     onMenuHide : function(e){
26236         this.el.removeClass("x-btn-menu-active");
26237     }   
26238 });
26239
26240 // Private utility class used by Button
26241 Roo.ButtonToggleMgr = function(){
26242    var groups = {};
26243    
26244    function toggleGroup(btn, state){
26245        if(state){
26246            var g = groups[btn.toggleGroup];
26247            for(var i = 0, l = g.length; i < l; i++){
26248                if(g[i] != btn){
26249                    g[i].toggle(false);
26250                }
26251            }
26252        }
26253    }
26254    
26255    return {
26256        register : function(btn){
26257            if(!btn.toggleGroup){
26258                return;
26259            }
26260            var g = groups[btn.toggleGroup];
26261            if(!g){
26262                g = groups[btn.toggleGroup] = [];
26263            }
26264            g.push(btn);
26265            btn.on("toggle", toggleGroup);
26266        },
26267        
26268        unregister : function(btn){
26269            if(!btn.toggleGroup){
26270                return;
26271            }
26272            var g = groups[btn.toggleGroup];
26273            if(g){
26274                g.remove(btn);
26275                btn.un("toggle", toggleGroup);
26276            }
26277        }
26278    };
26279 }();/*
26280  * Based on:
26281  * Ext JS Library 1.1.1
26282  * Copyright(c) 2006-2007, Ext JS, LLC.
26283  *
26284  * Originally Released Under LGPL - original licence link has changed is not relivant.
26285  *
26286  * Fork - LGPL
26287  * <script type="text/javascript">
26288  */
26289  
26290 /**
26291  * @class Roo.SplitButton
26292  * @extends Roo.Button
26293  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26294  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26295  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26296  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26297  * @cfg {String} arrowTooltip The title attribute of the arrow
26298  * @constructor
26299  * Create a new menu button
26300  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26301  * @param {Object} config The config object
26302  */
26303 Roo.SplitButton = function(renderTo, config){
26304     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26305     /**
26306      * @event arrowclick
26307      * Fires when this button's arrow is clicked
26308      * @param {SplitButton} this
26309      * @param {EventObject} e The click event
26310      */
26311     this.addEvents({"arrowclick":true});
26312 };
26313
26314 Roo.extend(Roo.SplitButton, Roo.Button, {
26315     render : function(renderTo){
26316         // this is one sweet looking template!
26317         var tpl = new Roo.Template(
26318             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26319             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26320             '<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>',
26321             "</tbody></table></td><td>",
26322             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26323             '<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>',
26324             "</tbody></table></td></tr></table>"
26325         );
26326         var btn = tpl.append(renderTo, [this.text, this.type], true);
26327         var btnEl = btn.child("button");
26328         if(this.cls){
26329             btn.addClass(this.cls);
26330         }
26331         if(this.icon){
26332             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26333         }
26334         if(this.iconCls){
26335             btnEl.addClass(this.iconCls);
26336             if(!this.cls){
26337                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26338             }
26339         }
26340         this.el = btn;
26341         if(this.handleMouseEvents){
26342             btn.on("mouseover", this.onMouseOver, this);
26343             btn.on("mouseout", this.onMouseOut, this);
26344             btn.on("mousedown", this.onMouseDown, this);
26345             btn.on("mouseup", this.onMouseUp, this);
26346         }
26347         btn.on(this.clickEvent, this.onClick, this);
26348         if(this.tooltip){
26349             if(typeof this.tooltip == 'object'){
26350                 Roo.QuickTips.tips(Roo.apply({
26351                       target: btnEl.id
26352                 }, this.tooltip));
26353             } else {
26354                 btnEl.dom[this.tooltipType] = this.tooltip;
26355             }
26356         }
26357         if(this.arrowTooltip){
26358             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26359         }
26360         if(this.hidden){
26361             this.hide();
26362         }
26363         if(this.disabled){
26364             this.disable();
26365         }
26366         if(this.pressed){
26367             this.el.addClass("x-btn-pressed");
26368         }
26369         if(Roo.isIE && !Roo.isIE7){
26370             this.autoWidth.defer(1, this);
26371         }else{
26372             this.autoWidth();
26373         }
26374         if(this.menu){
26375             this.menu.on("show", this.onMenuShow, this);
26376             this.menu.on("hide", this.onMenuHide, this);
26377         }
26378         this.fireEvent('render', this);
26379     },
26380
26381     // private
26382     autoWidth : function(){
26383         if(this.el){
26384             var tbl = this.el.child("table:first");
26385             var tbl2 = this.el.child("table:last");
26386             this.el.setWidth("auto");
26387             tbl.setWidth("auto");
26388             if(Roo.isIE7 && Roo.isStrict){
26389                 var ib = this.el.child('button:first');
26390                 if(ib && ib.getWidth() > 20){
26391                     ib.clip();
26392                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26393                 }
26394             }
26395             if(this.minWidth){
26396                 if(this.hidden){
26397                     this.el.beginMeasure();
26398                 }
26399                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26400                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26401                 }
26402                 if(this.hidden){
26403                     this.el.endMeasure();
26404                 }
26405             }
26406             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26407         } 
26408     },
26409     /**
26410      * Sets this button's click handler
26411      * @param {Function} handler The function to call when the button is clicked
26412      * @param {Object} scope (optional) Scope for the function passed above
26413      */
26414     setHandler : function(handler, scope){
26415         this.handler = handler;
26416         this.scope = scope;  
26417     },
26418     
26419     /**
26420      * Sets this button's arrow click handler
26421      * @param {Function} handler The function to call when the arrow is clicked
26422      * @param {Object} scope (optional) Scope for the function passed above
26423      */
26424     setArrowHandler : function(handler, scope){
26425         this.arrowHandler = handler;
26426         this.scope = scope;  
26427     },
26428     
26429     /**
26430      * Focus the button
26431      */
26432     focus : function(){
26433         if(this.el){
26434             this.el.child("button:first").focus();
26435         }
26436     },
26437
26438     // private
26439     onClick : function(e){
26440         e.preventDefault();
26441         if(!this.disabled){
26442             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26443                 if(this.menu && !this.menu.isVisible()){
26444                     this.menu.show(this.el, this.menuAlign);
26445                 }
26446                 this.fireEvent("arrowclick", this, e);
26447                 if(this.arrowHandler){
26448                     this.arrowHandler.call(this.scope || this, this, e);
26449                 }
26450             }else{
26451                 this.fireEvent("click", this, e);
26452                 if(this.handler){
26453                     this.handler.call(this.scope || this, this, e);
26454                 }
26455             }
26456         }
26457     },
26458     // private
26459     onMouseDown : function(e){
26460         if(!this.disabled){
26461             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26462         }
26463     },
26464     // private
26465     onMouseUp : function(e){
26466         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26467     }   
26468 });
26469
26470
26471 // backwards compat
26472 Roo.MenuButton = Roo.SplitButton;/*
26473  * Based on:
26474  * Ext JS Library 1.1.1
26475  * Copyright(c) 2006-2007, Ext JS, LLC.
26476  *
26477  * Originally Released Under LGPL - original licence link has changed is not relivant.
26478  *
26479  * Fork - LGPL
26480  * <script type="text/javascript">
26481  */
26482
26483 /**
26484  * @class Roo.Toolbar
26485  * Basic Toolbar class.
26486  * @constructor
26487  * Creates a new Toolbar
26488  * @param {Object} config The config object
26489  */ 
26490 Roo.Toolbar = function(container, buttons, config)
26491 {
26492     /// old consturctor format still supported..
26493     if(container instanceof Array){ // omit the container for later rendering
26494         buttons = container;
26495         config = buttons;
26496         container = null;
26497     }
26498     if (typeof(container) == 'object' && container.xtype) {
26499         config = container;
26500         container = config.container;
26501         buttons = config.buttons; // not really - use items!!
26502     }
26503     var xitems = [];
26504     if (config && config.items) {
26505         xitems = config.items;
26506         delete config.items;
26507     }
26508     Roo.apply(this, config);
26509     this.buttons = buttons;
26510     
26511     if(container){
26512         this.render(container);
26513     }
26514     Roo.each(xitems, function(b) {
26515         this.add(b);
26516     }, this);
26517     
26518 };
26519
26520 Roo.Toolbar.prototype = {
26521     /**
26522      * @cfg {Roo.data.Store} items
26523      * array of button configs or elements to add
26524      */
26525     
26526     /**
26527      * @cfg {String/HTMLElement/Element} container
26528      * The id or element that will contain the toolbar
26529      */
26530     // private
26531     render : function(ct){
26532         this.el = Roo.get(ct);
26533         if(this.cls){
26534             this.el.addClass(this.cls);
26535         }
26536         // using a table allows for vertical alignment
26537         // 100% width is needed by Safari...
26538         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26539         this.tr = this.el.child("tr", true);
26540         var autoId = 0;
26541         this.items = new Roo.util.MixedCollection(false, function(o){
26542             return o.id || ("item" + (++autoId));
26543         });
26544         if(this.buttons){
26545             this.add.apply(this, this.buttons);
26546             delete this.buttons;
26547         }
26548     },
26549
26550     /**
26551      * Adds element(s) to the toolbar -- this function takes a variable number of 
26552      * arguments of mixed type and adds them to the toolbar.
26553      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26554      * <ul>
26555      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26556      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26557      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26558      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26559      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26560      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26561      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26562      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26563      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26564      * </ul>
26565      * @param {Mixed} arg2
26566      * @param {Mixed} etc.
26567      */
26568     add : function(){
26569         var a = arguments, l = a.length;
26570         for(var i = 0; i < l; i++){
26571             this._add(a[i]);
26572         }
26573     },
26574     // private..
26575     _add : function(el) {
26576         
26577         if (el.xtype) {
26578             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26579         }
26580         
26581         if (el.applyTo){ // some kind of form field
26582             return this.addField(el);
26583         } 
26584         if (el.render){ // some kind of Toolbar.Item
26585             return this.addItem(el);
26586         }
26587         if (typeof el == "string"){ // string
26588             if(el == "separator" || el == "-"){
26589                 return this.addSeparator();
26590             }
26591             if (el == " "){
26592                 return this.addSpacer();
26593             }
26594             if(el == "->"){
26595                 return this.addFill();
26596             }
26597             return this.addText(el);
26598             
26599         }
26600         if(el.tagName){ // element
26601             return this.addElement(el);
26602         }
26603         if(typeof el == "object"){ // must be button config?
26604             return this.addButton(el);
26605         }
26606         // and now what?!?!
26607         return false;
26608         
26609     },
26610     
26611     /**
26612      * Add an Xtype element
26613      * @param {Object} xtype Xtype Object
26614      * @return {Object} created Object
26615      */
26616     addxtype : function(e){
26617         return this.add(e);  
26618     },
26619     
26620     /**
26621      * Returns the Element for this toolbar.
26622      * @return {Roo.Element}
26623      */
26624     getEl : function(){
26625         return this.el;  
26626     },
26627     
26628     /**
26629      * Adds a separator
26630      * @return {Roo.Toolbar.Item} The separator item
26631      */
26632     addSeparator : function(){
26633         return this.addItem(new Roo.Toolbar.Separator());
26634     },
26635
26636     /**
26637      * Adds a spacer element
26638      * @return {Roo.Toolbar.Spacer} The spacer item
26639      */
26640     addSpacer : function(){
26641         return this.addItem(new Roo.Toolbar.Spacer());
26642     },
26643
26644     /**
26645      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26646      * @return {Roo.Toolbar.Fill} The fill item
26647      */
26648     addFill : function(){
26649         return this.addItem(new Roo.Toolbar.Fill());
26650     },
26651
26652     /**
26653      * Adds any standard HTML element to the toolbar
26654      * @param {String/HTMLElement/Element} el The element or id of the element to add
26655      * @return {Roo.Toolbar.Item} The element's item
26656      */
26657     addElement : function(el){
26658         return this.addItem(new Roo.Toolbar.Item(el));
26659     },
26660     /**
26661      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26662      * @type Roo.util.MixedCollection  
26663      */
26664     items : false,
26665      
26666     /**
26667      * Adds any Toolbar.Item or subclass
26668      * @param {Roo.Toolbar.Item} item
26669      * @return {Roo.Toolbar.Item} The item
26670      */
26671     addItem : function(item){
26672         var td = this.nextBlock();
26673         item.render(td);
26674         this.items.add(item);
26675         return item;
26676     },
26677     
26678     /**
26679      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26680      * @param {Object/Array} config A button config or array of configs
26681      * @return {Roo.Toolbar.Button/Array}
26682      */
26683     addButton : function(config){
26684         if(config instanceof Array){
26685             var buttons = [];
26686             for(var i = 0, len = config.length; i < len; i++) {
26687                 buttons.push(this.addButton(config[i]));
26688             }
26689             return buttons;
26690         }
26691         var b = config;
26692         if(!(config instanceof Roo.Toolbar.Button)){
26693             b = config.split ?
26694                 new Roo.Toolbar.SplitButton(config) :
26695                 new Roo.Toolbar.Button(config);
26696         }
26697         var td = this.nextBlock();
26698         b.render(td);
26699         this.items.add(b);
26700         return b;
26701     },
26702     
26703     /**
26704      * Adds text to the toolbar
26705      * @param {String} text The text to add
26706      * @return {Roo.Toolbar.Item} The element's item
26707      */
26708     addText : function(text){
26709         return this.addItem(new Roo.Toolbar.TextItem(text));
26710     },
26711     
26712     /**
26713      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26714      * @param {Number} index The index where the item is to be inserted
26715      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26716      * @return {Roo.Toolbar.Button/Item}
26717      */
26718     insertButton : function(index, item){
26719         if(item instanceof Array){
26720             var buttons = [];
26721             for(var i = 0, len = item.length; i < len; i++) {
26722                buttons.push(this.insertButton(index + i, item[i]));
26723             }
26724             return buttons;
26725         }
26726         if (!(item instanceof Roo.Toolbar.Button)){
26727            item = new Roo.Toolbar.Button(item);
26728         }
26729         var td = document.createElement("td");
26730         this.tr.insertBefore(td, this.tr.childNodes[index]);
26731         item.render(td);
26732         this.items.insert(index, item);
26733         return item;
26734     },
26735     
26736     /**
26737      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26738      * @param {Object} config
26739      * @return {Roo.Toolbar.Item} The element's item
26740      */
26741     addDom : function(config, returnEl){
26742         var td = this.nextBlock();
26743         Roo.DomHelper.overwrite(td, config);
26744         var ti = new Roo.Toolbar.Item(td.firstChild);
26745         ti.render(td);
26746         this.items.add(ti);
26747         return ti;
26748     },
26749
26750     /**
26751      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26752      * @type Roo.util.MixedCollection  
26753      */
26754     fields : false,
26755     
26756     /**
26757      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26758      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26759      * @param {Roo.form.Field} field
26760      * @return {Roo.ToolbarItem}
26761      */
26762      
26763       
26764     addField : function(field) {
26765         if (!this.fields) {
26766             var autoId = 0;
26767             this.fields = new Roo.util.MixedCollection(false, function(o){
26768                 return o.id || ("item" + (++autoId));
26769             });
26770
26771         }
26772         
26773         var td = this.nextBlock();
26774         field.render(td);
26775         var ti = new Roo.Toolbar.Item(td.firstChild);
26776         ti.render(td);
26777         this.items.add(ti);
26778         this.fields.add(field);
26779         return ti;
26780     },
26781     /**
26782      * Hide the toolbar
26783      * @method hide
26784      */
26785      
26786       
26787     hide : function()
26788     {
26789         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26790         this.el.child('div').hide();
26791     },
26792     /**
26793      * Show the toolbar
26794      * @method show
26795      */
26796     show : function()
26797     {
26798         this.el.child('div').show();
26799     },
26800       
26801     // private
26802     nextBlock : function(){
26803         var td = document.createElement("td");
26804         this.tr.appendChild(td);
26805         return td;
26806     },
26807
26808     // private
26809     destroy : function(){
26810         if(this.items){ // rendered?
26811             Roo.destroy.apply(Roo, this.items.items);
26812         }
26813         if(this.fields){ // rendered?
26814             Roo.destroy.apply(Roo, this.fields.items);
26815         }
26816         Roo.Element.uncache(this.el, this.tr);
26817     }
26818 };
26819
26820 /**
26821  * @class Roo.Toolbar.Item
26822  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26823  * @constructor
26824  * Creates a new Item
26825  * @param {HTMLElement} el 
26826  */
26827 Roo.Toolbar.Item = function(el){
26828     this.el = Roo.getDom(el);
26829     this.id = Roo.id(this.el);
26830     this.hidden = false;
26831 };
26832
26833 Roo.Toolbar.Item.prototype = {
26834     
26835     /**
26836      * Get this item's HTML Element
26837      * @return {HTMLElement}
26838      */
26839     getEl : function(){
26840        return this.el;  
26841     },
26842
26843     // private
26844     render : function(td){
26845         this.td = td;
26846         td.appendChild(this.el);
26847     },
26848     
26849     /**
26850      * Removes and destroys this item.
26851      */
26852     destroy : function(){
26853         this.td.parentNode.removeChild(this.td);
26854     },
26855     
26856     /**
26857      * Shows this item.
26858      */
26859     show: function(){
26860         this.hidden = false;
26861         this.td.style.display = "";
26862     },
26863     
26864     /**
26865      * Hides this item.
26866      */
26867     hide: function(){
26868         this.hidden = true;
26869         this.td.style.display = "none";
26870     },
26871     
26872     /**
26873      * Convenience function for boolean show/hide.
26874      * @param {Boolean} visible true to show/false to hide
26875      */
26876     setVisible: function(visible){
26877         if(visible) {
26878             this.show();
26879         }else{
26880             this.hide();
26881         }
26882     },
26883     
26884     /**
26885      * Try to focus this item.
26886      */
26887     focus : function(){
26888         Roo.fly(this.el).focus();
26889     },
26890     
26891     /**
26892      * Disables this item.
26893      */
26894     disable : function(){
26895         Roo.fly(this.td).addClass("x-item-disabled");
26896         this.disabled = true;
26897         this.el.disabled = true;
26898     },
26899     
26900     /**
26901      * Enables this item.
26902      */
26903     enable : function(){
26904         Roo.fly(this.td).removeClass("x-item-disabled");
26905         this.disabled = false;
26906         this.el.disabled = false;
26907     }
26908 };
26909
26910
26911 /**
26912  * @class Roo.Toolbar.Separator
26913  * @extends Roo.Toolbar.Item
26914  * A simple toolbar separator class
26915  * @constructor
26916  * Creates a new Separator
26917  */
26918 Roo.Toolbar.Separator = function(){
26919     var s = document.createElement("span");
26920     s.className = "ytb-sep";
26921     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26922 };
26923 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26924     enable:Roo.emptyFn,
26925     disable:Roo.emptyFn,
26926     focus:Roo.emptyFn
26927 });
26928
26929 /**
26930  * @class Roo.Toolbar.Spacer
26931  * @extends Roo.Toolbar.Item
26932  * A simple element that adds extra horizontal space to a toolbar.
26933  * @constructor
26934  * Creates a new Spacer
26935  */
26936 Roo.Toolbar.Spacer = function(){
26937     var s = document.createElement("div");
26938     s.className = "ytb-spacer";
26939     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26940 };
26941 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26942     enable:Roo.emptyFn,
26943     disable:Roo.emptyFn,
26944     focus:Roo.emptyFn
26945 });
26946
26947 /**
26948  * @class Roo.Toolbar.Fill
26949  * @extends Roo.Toolbar.Spacer
26950  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26951  * @constructor
26952  * Creates a new Spacer
26953  */
26954 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26955     // private
26956     render : function(td){
26957         td.style.width = '100%';
26958         Roo.Toolbar.Fill.superclass.render.call(this, td);
26959     }
26960 });
26961
26962 /**
26963  * @class Roo.Toolbar.TextItem
26964  * @extends Roo.Toolbar.Item
26965  * A simple class that renders text directly into a toolbar.
26966  * @constructor
26967  * Creates a new TextItem
26968  * @param {String} text
26969  */
26970 Roo.Toolbar.TextItem = function(text){
26971     if (typeof(text) == 'object') {
26972         text = text.text;
26973     }
26974     var s = document.createElement("span");
26975     s.className = "ytb-text";
26976     s.innerHTML = text;
26977     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26978 };
26979 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26980     enable:Roo.emptyFn,
26981     disable:Roo.emptyFn,
26982     focus:Roo.emptyFn
26983 });
26984
26985 /**
26986  * @class Roo.Toolbar.Button
26987  * @extends Roo.Button
26988  * A button that renders into a toolbar.
26989  * @constructor
26990  * Creates a new Button
26991  * @param {Object} config A standard {@link Roo.Button} config object
26992  */
26993 Roo.Toolbar.Button = function(config){
26994     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26995 };
26996 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26997     render : function(td){
26998         this.td = td;
26999         Roo.Toolbar.Button.superclass.render.call(this, td);
27000     },
27001     
27002     /**
27003      * Removes and destroys this button
27004      */
27005     destroy : function(){
27006         Roo.Toolbar.Button.superclass.destroy.call(this);
27007         this.td.parentNode.removeChild(this.td);
27008     },
27009     
27010     /**
27011      * Shows this button
27012      */
27013     show: function(){
27014         this.hidden = false;
27015         this.td.style.display = "";
27016     },
27017     
27018     /**
27019      * Hides this button
27020      */
27021     hide: function(){
27022         this.hidden = true;
27023         this.td.style.display = "none";
27024     },
27025
27026     /**
27027      * Disables this item
27028      */
27029     disable : function(){
27030         Roo.fly(this.td).addClass("x-item-disabled");
27031         this.disabled = true;
27032     },
27033
27034     /**
27035      * Enables this item
27036      */
27037     enable : function(){
27038         Roo.fly(this.td).removeClass("x-item-disabled");
27039         this.disabled = false;
27040     }
27041 });
27042 // backwards compat
27043 Roo.ToolbarButton = Roo.Toolbar.Button;
27044
27045 /**
27046  * @class Roo.Toolbar.SplitButton
27047  * @extends Roo.SplitButton
27048  * A menu button that renders into a toolbar.
27049  * @constructor
27050  * Creates a new SplitButton
27051  * @param {Object} config A standard {@link Roo.SplitButton} config object
27052  */
27053 Roo.Toolbar.SplitButton = function(config){
27054     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27055 };
27056 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27057     render : function(td){
27058         this.td = td;
27059         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27060     },
27061     
27062     /**
27063      * Removes and destroys this button
27064      */
27065     destroy : function(){
27066         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27067         this.td.parentNode.removeChild(this.td);
27068     },
27069     
27070     /**
27071      * Shows this button
27072      */
27073     show: function(){
27074         this.hidden = false;
27075         this.td.style.display = "";
27076     },
27077     
27078     /**
27079      * Hides this button
27080      */
27081     hide: function(){
27082         this.hidden = true;
27083         this.td.style.display = "none";
27084     }
27085 });
27086
27087 // backwards compat
27088 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27089  * Based on:
27090  * Ext JS Library 1.1.1
27091  * Copyright(c) 2006-2007, Ext JS, LLC.
27092  *
27093  * Originally Released Under LGPL - original licence link has changed is not relivant.
27094  *
27095  * Fork - LGPL
27096  * <script type="text/javascript">
27097  */
27098  
27099 /**
27100  * @class Roo.PagingToolbar
27101  * @extends Roo.Toolbar
27102  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27103  * @constructor
27104  * Create a new PagingToolbar
27105  * @param {Object} config The config object
27106  */
27107 Roo.PagingToolbar = function(el, ds, config)
27108 {
27109     // old args format still supported... - xtype is prefered..
27110     if (typeof(el) == 'object' && el.xtype) {
27111         // created from xtype...
27112         config = el;
27113         ds = el.dataSource;
27114         el = config.container;
27115     }
27116     
27117     
27118     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27119     this.ds = ds;
27120     this.cursor = 0;
27121     this.renderButtons(this.el);
27122     this.bind(ds);
27123 };
27124
27125 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27126     /**
27127      * @cfg {Roo.data.Store} dataSource
27128      * The underlying data store providing the paged data
27129      */
27130     /**
27131      * @cfg {String/HTMLElement/Element} container
27132      * container The id or element that will contain the toolbar
27133      */
27134     /**
27135      * @cfg {Boolean} displayInfo
27136      * True to display the displayMsg (defaults to false)
27137      */
27138     /**
27139      * @cfg {Number} pageSize
27140      * The number of records to display per page (defaults to 20)
27141      */
27142     pageSize: 20,
27143     /**
27144      * @cfg {String} displayMsg
27145      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27146      */
27147     displayMsg : 'Displaying {0} - {1} of {2}',
27148     /**
27149      * @cfg {String} emptyMsg
27150      * The message to display when no records are found (defaults to "No data to display")
27151      */
27152     emptyMsg : 'No data to display',
27153     /**
27154      * Customizable piece of the default paging text (defaults to "Page")
27155      * @type String
27156      */
27157     beforePageText : "Page",
27158     /**
27159      * Customizable piece of the default paging text (defaults to "of %0")
27160      * @type String
27161      */
27162     afterPageText : "of {0}",
27163     /**
27164      * Customizable piece of the default paging text (defaults to "First Page")
27165      * @type String
27166      */
27167     firstText : "First Page",
27168     /**
27169      * Customizable piece of the default paging text (defaults to "Previous Page")
27170      * @type String
27171      */
27172     prevText : "Previous Page",
27173     /**
27174      * Customizable piece of the default paging text (defaults to "Next Page")
27175      * @type String
27176      */
27177     nextText : "Next Page",
27178     /**
27179      * Customizable piece of the default paging text (defaults to "Last Page")
27180      * @type String
27181      */
27182     lastText : "Last Page",
27183     /**
27184      * Customizable piece of the default paging text (defaults to "Refresh")
27185      * @type String
27186      */
27187     refreshText : "Refresh",
27188
27189     // private
27190     renderButtons : function(el){
27191         Roo.PagingToolbar.superclass.render.call(this, el);
27192         this.first = this.addButton({
27193             tooltip: this.firstText,
27194             cls: "x-btn-icon x-grid-page-first",
27195             disabled: true,
27196             handler: this.onClick.createDelegate(this, ["first"])
27197         });
27198         this.prev = this.addButton({
27199             tooltip: this.prevText,
27200             cls: "x-btn-icon x-grid-page-prev",
27201             disabled: true,
27202             handler: this.onClick.createDelegate(this, ["prev"])
27203         });
27204         //this.addSeparator();
27205         this.add(this.beforePageText);
27206         this.field = Roo.get(this.addDom({
27207            tag: "input",
27208            type: "text",
27209            size: "3",
27210            value: "1",
27211            cls: "x-grid-page-number"
27212         }).el);
27213         this.field.on("keydown", this.onPagingKeydown, this);
27214         this.field.on("focus", function(){this.dom.select();});
27215         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27216         this.field.setHeight(18);
27217         //this.addSeparator();
27218         this.next = this.addButton({
27219             tooltip: this.nextText,
27220             cls: "x-btn-icon x-grid-page-next",
27221             disabled: true,
27222             handler: this.onClick.createDelegate(this, ["next"])
27223         });
27224         this.last = this.addButton({
27225             tooltip: this.lastText,
27226             cls: "x-btn-icon x-grid-page-last",
27227             disabled: true,
27228             handler: this.onClick.createDelegate(this, ["last"])
27229         });
27230         //this.addSeparator();
27231         this.loading = this.addButton({
27232             tooltip: this.refreshText,
27233             cls: "x-btn-icon x-grid-loading",
27234             handler: this.onClick.createDelegate(this, ["refresh"])
27235         });
27236
27237         if(this.displayInfo){
27238             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27239         }
27240     },
27241
27242     // private
27243     updateInfo : function(){
27244         if(this.displayEl){
27245             var count = this.ds.getCount();
27246             var msg = count == 0 ?
27247                 this.emptyMsg :
27248                 String.format(
27249                     this.displayMsg,
27250                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27251                 );
27252             this.displayEl.update(msg);
27253         }
27254     },
27255
27256     // private
27257     onLoad : function(ds, r, o){
27258        this.cursor = o.params ? o.params.start : 0;
27259        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27260
27261        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27262        this.field.dom.value = ap;
27263        this.first.setDisabled(ap == 1);
27264        this.prev.setDisabled(ap == 1);
27265        this.next.setDisabled(ap == ps);
27266        this.last.setDisabled(ap == ps);
27267        this.loading.enable();
27268        this.updateInfo();
27269     },
27270
27271     // private
27272     getPageData : function(){
27273         var total = this.ds.getTotalCount();
27274         return {
27275             total : total,
27276             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27277             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27278         };
27279     },
27280
27281     // private
27282     onLoadError : function(){
27283         this.loading.enable();
27284     },
27285
27286     // private
27287     onPagingKeydown : function(e){
27288         var k = e.getKey();
27289         var d = this.getPageData();
27290         if(k == e.RETURN){
27291             var v = this.field.dom.value, pageNum;
27292             if(!v || isNaN(pageNum = parseInt(v, 10))){
27293                 this.field.dom.value = d.activePage;
27294                 return;
27295             }
27296             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27297             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27298             e.stopEvent();
27299         }
27300         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))
27301         {
27302           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27303           this.field.dom.value = pageNum;
27304           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27305           e.stopEvent();
27306         }
27307         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27308         {
27309           var v = this.field.dom.value, pageNum; 
27310           var increment = (e.shiftKey) ? 10 : 1;
27311           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27312             increment *= -1;
27313           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27314             this.field.dom.value = d.activePage;
27315             return;
27316           }
27317           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27318           {
27319             this.field.dom.value = parseInt(v, 10) + increment;
27320             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27321             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27322           }
27323           e.stopEvent();
27324         }
27325     },
27326
27327     // private
27328     beforeLoad : function(){
27329         if(this.loading){
27330             this.loading.disable();
27331         }
27332     },
27333
27334     // private
27335     onClick : function(which){
27336         var ds = this.ds;
27337         switch(which){
27338             case "first":
27339                 ds.load({params:{start: 0, limit: this.pageSize}});
27340             break;
27341             case "prev":
27342                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27343             break;
27344             case "next":
27345                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27346             break;
27347             case "last":
27348                 var total = ds.getTotalCount();
27349                 var extra = total % this.pageSize;
27350                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27351                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27352             break;
27353             case "refresh":
27354                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27355             break;
27356         }
27357     },
27358
27359     /**
27360      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27361      * @param {Roo.data.Store} store The data store to unbind
27362      */
27363     unbind : function(ds){
27364         ds.un("beforeload", this.beforeLoad, this);
27365         ds.un("load", this.onLoad, this);
27366         ds.un("loadexception", this.onLoadError, this);
27367         ds.un("remove", this.updateInfo, this);
27368         ds.un("add", this.updateInfo, this);
27369         this.ds = undefined;
27370     },
27371
27372     /**
27373      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27374      * @param {Roo.data.Store} store The data store to bind
27375      */
27376     bind : function(ds){
27377         ds.on("beforeload", this.beforeLoad, this);
27378         ds.on("load", this.onLoad, this);
27379         ds.on("loadexception", this.onLoadError, this);
27380         ds.on("remove", this.updateInfo, this);
27381         ds.on("add", this.updateInfo, this);
27382         this.ds = ds;
27383     }
27384 });/*
27385  * Based on:
27386  * Ext JS Library 1.1.1
27387  * Copyright(c) 2006-2007, Ext JS, LLC.
27388  *
27389  * Originally Released Under LGPL - original licence link has changed is not relivant.
27390  *
27391  * Fork - LGPL
27392  * <script type="text/javascript">
27393  */
27394
27395 /**
27396  * @class Roo.Resizable
27397  * @extends Roo.util.Observable
27398  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27399  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27400  * 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
27401  * the element will be wrapped for you automatically.</p>
27402  * <p>Here is the list of valid resize handles:</p>
27403  * <pre>
27404 Value   Description
27405 ------  -------------------
27406  'n'     north
27407  's'     south
27408  'e'     east
27409  'w'     west
27410  'nw'    northwest
27411  'sw'    southwest
27412  'se'    southeast
27413  'ne'    northeast
27414  'hd'    horizontal drag
27415  'all'   all
27416 </pre>
27417  * <p>Here's an example showing the creation of a typical Resizable:</p>
27418  * <pre><code>
27419 var resizer = new Roo.Resizable("element-id", {
27420     handles: 'all',
27421     minWidth: 200,
27422     minHeight: 100,
27423     maxWidth: 500,
27424     maxHeight: 400,
27425     pinned: true
27426 });
27427 resizer.on("resize", myHandler);
27428 </code></pre>
27429  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27430  * resizer.east.setDisplayed(false);</p>
27431  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27432  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27433  * resize operation's new size (defaults to [0, 0])
27434  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27435  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27436  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27437  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27438  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27439  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27440  * @cfg {Number} width The width of the element in pixels (defaults to null)
27441  * @cfg {Number} height The height of the element in pixels (defaults to null)
27442  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27443  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27444  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27445  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27446  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27447  * in favor of the handles config option (defaults to false)
27448  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27449  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27450  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27451  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27452  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27453  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27454  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27455  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27456  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27457  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27458  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27459  * @constructor
27460  * Create a new resizable component
27461  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27462  * @param {Object} config configuration options
27463   */
27464 Roo.Resizable = function(el, config)
27465 {
27466     this.el = Roo.get(el);
27467
27468     if(config && config.wrap){
27469         config.resizeChild = this.el;
27470         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27471         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27472         this.el.setStyle("overflow", "hidden");
27473         this.el.setPositioning(config.resizeChild.getPositioning());
27474         config.resizeChild.clearPositioning();
27475         if(!config.width || !config.height){
27476             var csize = config.resizeChild.getSize();
27477             this.el.setSize(csize.width, csize.height);
27478         }
27479         if(config.pinned && !config.adjustments){
27480             config.adjustments = "auto";
27481         }
27482     }
27483
27484     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27485     this.proxy.unselectable();
27486     this.proxy.enableDisplayMode('block');
27487
27488     Roo.apply(this, config);
27489
27490     if(this.pinned){
27491         this.disableTrackOver = true;
27492         this.el.addClass("x-resizable-pinned");
27493     }
27494     // if the element isn't positioned, make it relative
27495     var position = this.el.getStyle("position");
27496     if(position != "absolute" && position != "fixed"){
27497         this.el.setStyle("position", "relative");
27498     }
27499     if(!this.handles){ // no handles passed, must be legacy style
27500         this.handles = 's,e,se';
27501         if(this.multiDirectional){
27502             this.handles += ',n,w';
27503         }
27504     }
27505     if(this.handles == "all"){
27506         this.handles = "n s e w ne nw se sw";
27507     }
27508     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27509     var ps = Roo.Resizable.positions;
27510     for(var i = 0, len = hs.length; i < len; i++){
27511         if(hs[i] && ps[hs[i]]){
27512             var pos = ps[hs[i]];
27513             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27514         }
27515     }
27516     // legacy
27517     this.corner = this.southeast;
27518     
27519     // updateBox = the box can move..
27520     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27521         this.updateBox = true;
27522     }
27523
27524     this.activeHandle = null;
27525
27526     if(this.resizeChild){
27527         if(typeof this.resizeChild == "boolean"){
27528             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27529         }else{
27530             this.resizeChild = Roo.get(this.resizeChild, true);
27531         }
27532     }
27533     
27534     if(this.adjustments == "auto"){
27535         var rc = this.resizeChild;
27536         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27537         if(rc && (hw || hn)){
27538             rc.position("relative");
27539             rc.setLeft(hw ? hw.el.getWidth() : 0);
27540             rc.setTop(hn ? hn.el.getHeight() : 0);
27541         }
27542         this.adjustments = [
27543             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27544             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27545         ];
27546     }
27547
27548     if(this.draggable){
27549         this.dd = this.dynamic ?
27550             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27551         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27552     }
27553
27554     // public events
27555     this.addEvents({
27556         /**
27557          * @event beforeresize
27558          * Fired before resize is allowed. Set enabled to false to cancel resize.
27559          * @param {Roo.Resizable} this
27560          * @param {Roo.EventObject} e The mousedown event
27561          */
27562         "beforeresize" : true,
27563         /**
27564          * @event resize
27565          * Fired after a resize.
27566          * @param {Roo.Resizable} this
27567          * @param {Number} width The new width
27568          * @param {Number} height The new height
27569          * @param {Roo.EventObject} e The mouseup event
27570          */
27571         "resize" : true
27572     });
27573
27574     if(this.width !== null && this.height !== null){
27575         this.resizeTo(this.width, this.height);
27576     }else{
27577         this.updateChildSize();
27578     }
27579     if(Roo.isIE){
27580         this.el.dom.style.zoom = 1;
27581     }
27582     Roo.Resizable.superclass.constructor.call(this);
27583 };
27584
27585 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27586         resizeChild : false,
27587         adjustments : [0, 0],
27588         minWidth : 5,
27589         minHeight : 5,
27590         maxWidth : 10000,
27591         maxHeight : 10000,
27592         enabled : true,
27593         animate : false,
27594         duration : .35,
27595         dynamic : false,
27596         handles : false,
27597         multiDirectional : false,
27598         disableTrackOver : false,
27599         easing : 'easeOutStrong',
27600         widthIncrement : 0,
27601         heightIncrement : 0,
27602         pinned : false,
27603         width : null,
27604         height : null,
27605         preserveRatio : false,
27606         transparent: false,
27607         minX: 0,
27608         minY: 0,
27609         draggable: false,
27610
27611         /**
27612          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27613          */
27614         constrainTo: undefined,
27615         /**
27616          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27617          */
27618         resizeRegion: undefined,
27619
27620
27621     /**
27622      * Perform a manual resize
27623      * @param {Number} width
27624      * @param {Number} height
27625      */
27626     resizeTo : function(width, height){
27627         this.el.setSize(width, height);
27628         this.updateChildSize();
27629         this.fireEvent("resize", this, width, height, null);
27630     },
27631
27632     // private
27633     startSizing : function(e, handle){
27634         this.fireEvent("beforeresize", this, e);
27635         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27636
27637             if(!this.overlay){
27638                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27639                 this.overlay.unselectable();
27640                 this.overlay.enableDisplayMode("block");
27641                 this.overlay.on("mousemove", this.onMouseMove, this);
27642                 this.overlay.on("mouseup", this.onMouseUp, this);
27643             }
27644             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27645
27646             this.resizing = true;
27647             this.startBox = this.el.getBox();
27648             this.startPoint = e.getXY();
27649             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27650                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27651
27652             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27653             this.overlay.show();
27654
27655             if(this.constrainTo) {
27656                 var ct = Roo.get(this.constrainTo);
27657                 this.resizeRegion = ct.getRegion().adjust(
27658                     ct.getFrameWidth('t'),
27659                     ct.getFrameWidth('l'),
27660                     -ct.getFrameWidth('b'),
27661                     -ct.getFrameWidth('r')
27662                 );
27663             }
27664
27665             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27666             this.proxy.show();
27667             this.proxy.setBox(this.startBox);
27668             if(!this.dynamic){
27669                 this.proxy.setStyle('visibility', 'visible');
27670             }
27671         }
27672     },
27673
27674     // private
27675     onMouseDown : function(handle, e){
27676         if(this.enabled){
27677             e.stopEvent();
27678             this.activeHandle = handle;
27679             this.startSizing(e, handle);
27680         }
27681     },
27682
27683     // private
27684     onMouseUp : function(e){
27685         var size = this.resizeElement();
27686         this.resizing = false;
27687         this.handleOut();
27688         this.overlay.hide();
27689         this.proxy.hide();
27690         this.fireEvent("resize", this, size.width, size.height, e);
27691     },
27692
27693     // private
27694     updateChildSize : function(){
27695         if(this.resizeChild){
27696             var el = this.el;
27697             var child = this.resizeChild;
27698             var adj = this.adjustments;
27699             if(el.dom.offsetWidth){
27700                 var b = el.getSize(true);
27701                 child.setSize(b.width+adj[0], b.height+adj[1]);
27702             }
27703             // Second call here for IE
27704             // The first call enables instant resizing and
27705             // the second call corrects scroll bars if they
27706             // exist
27707             if(Roo.isIE){
27708                 setTimeout(function(){
27709                     if(el.dom.offsetWidth){
27710                         var b = el.getSize(true);
27711                         child.setSize(b.width+adj[0], b.height+adj[1]);
27712                     }
27713                 }, 10);
27714             }
27715         }
27716     },
27717
27718     // private
27719     snap : function(value, inc, min){
27720         if(!inc || !value) return value;
27721         var newValue = value;
27722         var m = value % inc;
27723         if(m > 0){
27724             if(m > (inc/2)){
27725                 newValue = value + (inc-m);
27726             }else{
27727                 newValue = value - m;
27728             }
27729         }
27730         return Math.max(min, newValue);
27731     },
27732
27733     // private
27734     resizeElement : function(){
27735         var box = this.proxy.getBox();
27736         if(this.updateBox){
27737             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27738         }else{
27739             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27740         }
27741         this.updateChildSize();
27742         if(!this.dynamic){
27743             this.proxy.hide();
27744         }
27745         return box;
27746     },
27747
27748     // private
27749     constrain : function(v, diff, m, mx){
27750         if(v - diff < m){
27751             diff = v - m;
27752         }else if(v - diff > mx){
27753             diff = mx - v;
27754         }
27755         return diff;
27756     },
27757
27758     // private
27759     onMouseMove : function(e){
27760         if(this.enabled){
27761             try{// try catch so if something goes wrong the user doesn't get hung
27762
27763             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27764                 return;
27765             }
27766
27767             //var curXY = this.startPoint;
27768             var curSize = this.curSize || this.startBox;
27769             var x = this.startBox.x, y = this.startBox.y;
27770             var ox = x, oy = y;
27771             var w = curSize.width, h = curSize.height;
27772             var ow = w, oh = h;
27773             var mw = this.minWidth, mh = this.minHeight;
27774             var mxw = this.maxWidth, mxh = this.maxHeight;
27775             var wi = this.widthIncrement;
27776             var hi = this.heightIncrement;
27777
27778             var eventXY = e.getXY();
27779             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27780             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27781
27782             var pos = this.activeHandle.position;
27783
27784             switch(pos){
27785                 case "east":
27786                     w += diffX;
27787                     w = Math.min(Math.max(mw, w), mxw);
27788                     break;
27789              
27790                 case "south":
27791                     h += diffY;
27792                     h = Math.min(Math.max(mh, h), mxh);
27793                     break;
27794                 case "southeast":
27795                     w += diffX;
27796                     h += diffY;
27797                     w = Math.min(Math.max(mw, w), mxw);
27798                     h = Math.min(Math.max(mh, h), mxh);
27799                     break;
27800                 case "north":
27801                     diffY = this.constrain(h, diffY, mh, mxh);
27802                     y += diffY;
27803                     h -= diffY;
27804                     break;
27805                 case "hdrag":
27806                     
27807                     if (wi) {
27808                         var adiffX = Math.abs(diffX);
27809                         var sub = (adiffX % wi); // how much 
27810                         if (sub > (wi/2)) { // far enough to snap
27811                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27812                         } else {
27813                             // remove difference.. 
27814                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27815                         }
27816                     }
27817                     x += diffX;
27818                     x = Math.max(this.minX, x);
27819                     break;
27820                 case "west":
27821                     diffX = this.constrain(w, diffX, mw, mxw);
27822                     x += diffX;
27823                     w -= diffX;
27824                     break;
27825                 case "northeast":
27826                     w += diffX;
27827                     w = Math.min(Math.max(mw, w), mxw);
27828                     diffY = this.constrain(h, diffY, mh, mxh);
27829                     y += diffY;
27830                     h -= diffY;
27831                     break;
27832                 case "northwest":
27833                     diffX = this.constrain(w, diffX, mw, mxw);
27834                     diffY = this.constrain(h, diffY, mh, mxh);
27835                     y += diffY;
27836                     h -= diffY;
27837                     x += diffX;
27838                     w -= diffX;
27839                     break;
27840                case "southwest":
27841                     diffX = this.constrain(w, diffX, mw, mxw);
27842                     h += diffY;
27843                     h = Math.min(Math.max(mh, h), mxh);
27844                     x += diffX;
27845                     w -= diffX;
27846                     break;
27847             }
27848
27849             var sw = this.snap(w, wi, mw);
27850             var sh = this.snap(h, hi, mh);
27851             if(sw != w || sh != h){
27852                 switch(pos){
27853                     case "northeast":
27854                         y -= sh - h;
27855                     break;
27856                     case "north":
27857                         y -= sh - h;
27858                         break;
27859                     case "southwest":
27860                         x -= sw - w;
27861                     break;
27862                     case "west":
27863                         x -= sw - w;
27864                         break;
27865                     case "northwest":
27866                         x -= sw - w;
27867                         y -= sh - h;
27868                     break;
27869                 }
27870                 w = sw;
27871                 h = sh;
27872             }
27873
27874             if(this.preserveRatio){
27875                 switch(pos){
27876                     case "southeast":
27877                     case "east":
27878                         h = oh * (w/ow);
27879                         h = Math.min(Math.max(mh, h), mxh);
27880                         w = ow * (h/oh);
27881                        break;
27882                     case "south":
27883                         w = ow * (h/oh);
27884                         w = Math.min(Math.max(mw, w), mxw);
27885                         h = oh * (w/ow);
27886                         break;
27887                     case "northeast":
27888                         w = ow * (h/oh);
27889                         w = Math.min(Math.max(mw, w), mxw);
27890                         h = oh * (w/ow);
27891                     break;
27892                     case "north":
27893                         var tw = w;
27894                         w = ow * (h/oh);
27895                         w = Math.min(Math.max(mw, w), mxw);
27896                         h = oh * (w/ow);
27897                         x += (tw - w) / 2;
27898                         break;
27899                     case "southwest":
27900                         h = oh * (w/ow);
27901                         h = Math.min(Math.max(mh, h), mxh);
27902                         var tw = w;
27903                         w = ow * (h/oh);
27904                         x += tw - w;
27905                         break;
27906                     case "west":
27907                         var th = h;
27908                         h = oh * (w/ow);
27909                         h = Math.min(Math.max(mh, h), mxh);
27910                         y += (th - h) / 2;
27911                         var tw = w;
27912                         w = ow * (h/oh);
27913                         x += tw - w;
27914                        break;
27915                     case "northwest":
27916                         var tw = w;
27917                         var th = h;
27918                         h = oh * (w/ow);
27919                         h = Math.min(Math.max(mh, h), mxh);
27920                         w = ow * (h/oh);
27921                         y += th - h;
27922                         x += tw - w;
27923                        break;
27924
27925                 }
27926             }
27927             if (pos == 'hdrag') {
27928                 w = ow;
27929             }
27930             this.proxy.setBounds(x, y, w, h);
27931             if(this.dynamic){
27932                 this.resizeElement();
27933             }
27934             }catch(e){}
27935         }
27936     },
27937
27938     // private
27939     handleOver : function(){
27940         if(this.enabled){
27941             this.el.addClass("x-resizable-over");
27942         }
27943     },
27944
27945     // private
27946     handleOut : function(){
27947         if(!this.resizing){
27948             this.el.removeClass("x-resizable-over");
27949         }
27950     },
27951
27952     /**
27953      * Returns the element this component is bound to.
27954      * @return {Roo.Element}
27955      */
27956     getEl : function(){
27957         return this.el;
27958     },
27959
27960     /**
27961      * Returns the resizeChild element (or null).
27962      * @return {Roo.Element}
27963      */
27964     getResizeChild : function(){
27965         return this.resizeChild;
27966     },
27967
27968     /**
27969      * Destroys this resizable. If the element was wrapped and
27970      * removeEl is not true then the element remains.
27971      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27972      */
27973     destroy : function(removeEl){
27974         this.proxy.remove();
27975         if(this.overlay){
27976             this.overlay.removeAllListeners();
27977             this.overlay.remove();
27978         }
27979         var ps = Roo.Resizable.positions;
27980         for(var k in ps){
27981             if(typeof ps[k] != "function" && this[ps[k]]){
27982                 var h = this[ps[k]];
27983                 h.el.removeAllListeners();
27984                 h.el.remove();
27985             }
27986         }
27987         if(removeEl){
27988             this.el.update("");
27989             this.el.remove();
27990         }
27991     }
27992 });
27993
27994 // private
27995 // hash to map config positions to true positions
27996 Roo.Resizable.positions = {
27997     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
27998     hd: "hdrag"
27999 };
28000
28001 // private
28002 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28003     if(!this.tpl){
28004         // only initialize the template if resizable is used
28005         var tpl = Roo.DomHelper.createTemplate(
28006             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28007         );
28008         tpl.compile();
28009         Roo.Resizable.Handle.prototype.tpl = tpl;
28010     }
28011     this.position = pos;
28012     this.rz = rz;
28013     // show north drag fro topdra
28014     var handlepos = pos == 'hdrag' ? 'north' : pos;
28015     
28016     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28017     if (pos == 'hdrag') {
28018         this.el.setStyle('cursor', 'pointer');
28019     }
28020     this.el.unselectable();
28021     if(transparent){
28022         this.el.setOpacity(0);
28023     }
28024     this.el.on("mousedown", this.onMouseDown, this);
28025     if(!disableTrackOver){
28026         this.el.on("mouseover", this.onMouseOver, this);
28027         this.el.on("mouseout", this.onMouseOut, this);
28028     }
28029 };
28030
28031 // private
28032 Roo.Resizable.Handle.prototype = {
28033     afterResize : function(rz){
28034         // do nothing
28035     },
28036     // private
28037     onMouseDown : function(e){
28038         this.rz.onMouseDown(this, e);
28039     },
28040     // private
28041     onMouseOver : function(e){
28042         this.rz.handleOver(this, e);
28043     },
28044     // private
28045     onMouseOut : function(e){
28046         this.rz.handleOut(this, e);
28047     }
28048 };/*
28049  * Based on:
28050  * Ext JS Library 1.1.1
28051  * Copyright(c) 2006-2007, Ext JS, LLC.
28052  *
28053  * Originally Released Under LGPL - original licence link has changed is not relivant.
28054  *
28055  * Fork - LGPL
28056  * <script type="text/javascript">
28057  */
28058
28059 /**
28060  * @class Roo.Editor
28061  * @extends Roo.Component
28062  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28063  * @constructor
28064  * Create a new Editor
28065  * @param {Roo.form.Field} field The Field object (or descendant)
28066  * @param {Object} config The config object
28067  */
28068 Roo.Editor = function(field, config){
28069     Roo.Editor.superclass.constructor.call(this, config);
28070     this.field = field;
28071     this.addEvents({
28072         /**
28073              * @event beforestartedit
28074              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28075              * false from the handler of this event.
28076              * @param {Editor} this
28077              * @param {Roo.Element} boundEl The underlying element bound to this editor
28078              * @param {Mixed} value The field value being set
28079              */
28080         "beforestartedit" : true,
28081         /**
28082              * @event startedit
28083              * Fires when this editor is displayed
28084              * @param {Roo.Element} boundEl The underlying element bound to this editor
28085              * @param {Mixed} value The starting field value
28086              */
28087         "startedit" : true,
28088         /**
28089              * @event beforecomplete
28090              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28091              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28092              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28093              * event will not fire since no edit actually occurred.
28094              * @param {Editor} this
28095              * @param {Mixed} value The current field value
28096              * @param {Mixed} startValue The original field value
28097              */
28098         "beforecomplete" : true,
28099         /**
28100              * @event complete
28101              * Fires after editing is complete and any changed value has been written to the underlying field.
28102              * @param {Editor} this
28103              * @param {Mixed} value The current field value
28104              * @param {Mixed} startValue The original field value
28105              */
28106         "complete" : true,
28107         /**
28108          * @event specialkey
28109          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28110          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28111          * @param {Roo.form.Field} this
28112          * @param {Roo.EventObject} e The event object
28113          */
28114         "specialkey" : true
28115     });
28116 };
28117
28118 Roo.extend(Roo.Editor, Roo.Component, {
28119     /**
28120      * @cfg {Boolean/String} autosize
28121      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28122      * or "height" to adopt the height only (defaults to false)
28123      */
28124     /**
28125      * @cfg {Boolean} revertInvalid
28126      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28127      * validation fails (defaults to true)
28128      */
28129     /**
28130      * @cfg {Boolean} ignoreNoChange
28131      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28132      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28133      * will never be ignored.
28134      */
28135     /**
28136      * @cfg {Boolean} hideEl
28137      * False to keep the bound element visible while the editor is displayed (defaults to true)
28138      */
28139     /**
28140      * @cfg {Mixed} value
28141      * The data value of the underlying field (defaults to "")
28142      */
28143     value : "",
28144     /**
28145      * @cfg {String} alignment
28146      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28147      */
28148     alignment: "c-c?",
28149     /**
28150      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28151      * for bottom-right shadow (defaults to "frame")
28152      */
28153     shadow : "frame",
28154     /**
28155      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28156      */
28157     constrain : false,
28158     /**
28159      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28160      */
28161     completeOnEnter : false,
28162     /**
28163      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28164      */
28165     cancelOnEsc : false,
28166     /**
28167      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28168      */
28169     updateEl : false,
28170
28171     // private
28172     onRender : function(ct, position){
28173         this.el = new Roo.Layer({
28174             shadow: this.shadow,
28175             cls: "x-editor",
28176             parentEl : ct,
28177             shim : this.shim,
28178             shadowOffset:4,
28179             id: this.id,
28180             constrain: this.constrain
28181         });
28182         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28183         if(this.field.msgTarget != 'title'){
28184             this.field.msgTarget = 'qtip';
28185         }
28186         this.field.render(this.el);
28187         if(Roo.isGecko){
28188             this.field.el.dom.setAttribute('autocomplete', 'off');
28189         }
28190         this.field.on("specialkey", this.onSpecialKey, this);
28191         if(this.swallowKeys){
28192             this.field.el.swallowEvent(['keydown','keypress']);
28193         }
28194         this.field.show();
28195         this.field.on("blur", this.onBlur, this);
28196         if(this.field.grow){
28197             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28198         }
28199     },
28200
28201     onSpecialKey : function(field, e){
28202         //Roo.log('editor onSpecialKey');
28203         if(this.completeOnEnter && e.getKey() == e.ENTER){
28204             e.stopEvent();
28205             this.completeEdit();
28206         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28207             this.cancelEdit();
28208         }else{
28209             this.fireEvent('specialkey', field, e);
28210         }
28211     },
28212
28213     /**
28214      * Starts the editing process and shows the editor.
28215      * @param {String/HTMLElement/Element} el The element to edit
28216      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28217       * to the innerHTML of el.
28218      */
28219     startEdit : function(el, value){
28220         if(this.editing){
28221             this.completeEdit();
28222         }
28223         this.boundEl = Roo.get(el);
28224         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28225         if(!this.rendered){
28226             this.render(this.parentEl || document.body);
28227         }
28228         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28229             return;
28230         }
28231         this.startValue = v;
28232         this.field.setValue(v);
28233         if(this.autoSize){
28234             var sz = this.boundEl.getSize();
28235             switch(this.autoSize){
28236                 case "width":
28237                 this.setSize(sz.width,  "");
28238                 break;
28239                 case "height":
28240                 this.setSize("",  sz.height);
28241                 break;
28242                 default:
28243                 this.setSize(sz.width,  sz.height);
28244             }
28245         }
28246         this.el.alignTo(this.boundEl, this.alignment);
28247         this.editing = true;
28248         if(Roo.QuickTips){
28249             Roo.QuickTips.disable();
28250         }
28251         this.show();
28252     },
28253
28254     /**
28255      * Sets the height and width of this editor.
28256      * @param {Number} width The new width
28257      * @param {Number} height The new height
28258      */
28259     setSize : function(w, h){
28260         this.field.setSize(w, h);
28261         if(this.el){
28262             this.el.sync();
28263         }
28264     },
28265
28266     /**
28267      * Realigns the editor to the bound field based on the current alignment config value.
28268      */
28269     realign : function(){
28270         this.el.alignTo(this.boundEl, this.alignment);
28271     },
28272
28273     /**
28274      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28275      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28276      */
28277     completeEdit : function(remainVisible){
28278         if(!this.editing){
28279             return;
28280         }
28281         var v = this.getValue();
28282         if(this.revertInvalid !== false && !this.field.isValid()){
28283             v = this.startValue;
28284             this.cancelEdit(true);
28285         }
28286         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28287             this.editing = false;
28288             this.hide();
28289             return;
28290         }
28291         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28292             this.editing = false;
28293             if(this.updateEl && this.boundEl){
28294                 this.boundEl.update(v);
28295             }
28296             if(remainVisible !== true){
28297                 this.hide();
28298             }
28299             this.fireEvent("complete", this, v, this.startValue);
28300         }
28301     },
28302
28303     // private
28304     onShow : function(){
28305         this.el.show();
28306         if(this.hideEl !== false){
28307             this.boundEl.hide();
28308         }
28309         this.field.show();
28310         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28311             this.fixIEFocus = true;
28312             this.deferredFocus.defer(50, this);
28313         }else{
28314             this.field.focus();
28315         }
28316         this.fireEvent("startedit", this.boundEl, this.startValue);
28317     },
28318
28319     deferredFocus : function(){
28320         if(this.editing){
28321             this.field.focus();
28322         }
28323     },
28324
28325     /**
28326      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28327      * reverted to the original starting value.
28328      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28329      * cancel (defaults to false)
28330      */
28331     cancelEdit : function(remainVisible){
28332         if(this.editing){
28333             this.setValue(this.startValue);
28334             if(remainVisible !== true){
28335                 this.hide();
28336             }
28337         }
28338     },
28339
28340     // private
28341     onBlur : function(){
28342         if(this.allowBlur !== true && this.editing){
28343             this.completeEdit();
28344         }
28345     },
28346
28347     // private
28348     onHide : function(){
28349         if(this.editing){
28350             this.completeEdit();
28351             return;
28352         }
28353         this.field.blur();
28354         if(this.field.collapse){
28355             this.field.collapse();
28356         }
28357         this.el.hide();
28358         if(this.hideEl !== false){
28359             this.boundEl.show();
28360         }
28361         if(Roo.QuickTips){
28362             Roo.QuickTips.enable();
28363         }
28364     },
28365
28366     /**
28367      * Sets the data value of the editor
28368      * @param {Mixed} value Any valid value supported by the underlying field
28369      */
28370     setValue : function(v){
28371         this.field.setValue(v);
28372     },
28373
28374     /**
28375      * Gets the data value of the editor
28376      * @return {Mixed} The data value
28377      */
28378     getValue : function(){
28379         return this.field.getValue();
28380     }
28381 });/*
28382  * Based on:
28383  * Ext JS Library 1.1.1
28384  * Copyright(c) 2006-2007, Ext JS, LLC.
28385  *
28386  * Originally Released Under LGPL - original licence link has changed is not relivant.
28387  *
28388  * Fork - LGPL
28389  * <script type="text/javascript">
28390  */
28391  
28392 /**
28393  * @class Roo.BasicDialog
28394  * @extends Roo.util.Observable
28395  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28396  * <pre><code>
28397 var dlg = new Roo.BasicDialog("my-dlg", {
28398     height: 200,
28399     width: 300,
28400     minHeight: 100,
28401     minWidth: 150,
28402     modal: true,
28403     proxyDrag: true,
28404     shadow: true
28405 });
28406 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28407 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28408 dlg.addButton('Cancel', dlg.hide, dlg);
28409 dlg.show();
28410 </code></pre>
28411   <b>A Dialog should always be a direct child of the body element.</b>
28412  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28413  * @cfg {String} title Default text to display in the title bar (defaults to null)
28414  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28415  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28416  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28417  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28418  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28419  * (defaults to null with no animation)
28420  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28421  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28422  * property for valid values (defaults to 'all')
28423  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28424  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28425  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28426  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28427  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28428  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28429  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28430  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28431  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28432  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28433  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28434  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28435  * draggable = true (defaults to false)
28436  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28437  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28438  * shadow (defaults to false)
28439  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28440  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28441  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28442  * @cfg {Array} buttons Array of buttons
28443  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28444  * @constructor
28445  * Create a new BasicDialog.
28446  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28447  * @param {Object} config Configuration options
28448  */
28449 Roo.BasicDialog = function(el, config){
28450     this.el = Roo.get(el);
28451     var dh = Roo.DomHelper;
28452     if(!this.el && config && config.autoCreate){
28453         if(typeof config.autoCreate == "object"){
28454             if(!config.autoCreate.id){
28455                 config.autoCreate.id = el;
28456             }
28457             this.el = dh.append(document.body,
28458                         config.autoCreate, true);
28459         }else{
28460             this.el = dh.append(document.body,
28461                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28462         }
28463     }
28464     el = this.el;
28465     el.setDisplayed(true);
28466     el.hide = this.hideAction;
28467     this.id = el.id;
28468     el.addClass("x-dlg");
28469
28470     Roo.apply(this, config);
28471
28472     this.proxy = el.createProxy("x-dlg-proxy");
28473     this.proxy.hide = this.hideAction;
28474     this.proxy.setOpacity(.5);
28475     this.proxy.hide();
28476
28477     if(config.width){
28478         el.setWidth(config.width);
28479     }
28480     if(config.height){
28481         el.setHeight(config.height);
28482     }
28483     this.size = el.getSize();
28484     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28485         this.xy = [config.x,config.y];
28486     }else{
28487         this.xy = el.getCenterXY(true);
28488     }
28489     /** The header element @type Roo.Element */
28490     this.header = el.child("> .x-dlg-hd");
28491     /** The body element @type Roo.Element */
28492     this.body = el.child("> .x-dlg-bd");
28493     /** The footer element @type Roo.Element */
28494     this.footer = el.child("> .x-dlg-ft");
28495
28496     if(!this.header){
28497         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28498     }
28499     if(!this.body){
28500         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28501     }
28502
28503     this.header.unselectable();
28504     if(this.title){
28505         this.header.update(this.title);
28506     }
28507     // this element allows the dialog to be focused for keyboard event
28508     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28509     this.focusEl.swallowEvent("click", true);
28510
28511     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28512
28513     // wrap the body and footer for special rendering
28514     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28515     if(this.footer){
28516         this.bwrap.dom.appendChild(this.footer.dom);
28517     }
28518
28519     this.bg = this.el.createChild({
28520         tag: "div", cls:"x-dlg-bg",
28521         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28522     });
28523     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28524
28525
28526     if(this.autoScroll !== false && !this.autoTabs){
28527         this.body.setStyle("overflow", "auto");
28528     }
28529
28530     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28531
28532     if(this.closable !== false){
28533         this.el.addClass("x-dlg-closable");
28534         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28535         this.close.on("click", this.closeClick, this);
28536         this.close.addClassOnOver("x-dlg-close-over");
28537     }
28538     if(this.collapsible !== false){
28539         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28540         this.collapseBtn.on("click", this.collapseClick, this);
28541         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28542         this.header.on("dblclick", this.collapseClick, this);
28543     }
28544     if(this.resizable !== false){
28545         this.el.addClass("x-dlg-resizable");
28546         this.resizer = new Roo.Resizable(el, {
28547             minWidth: this.minWidth || 80,
28548             minHeight:this.minHeight || 80,
28549             handles: this.resizeHandles || "all",
28550             pinned: true
28551         });
28552         this.resizer.on("beforeresize", this.beforeResize, this);
28553         this.resizer.on("resize", this.onResize, this);
28554     }
28555     if(this.draggable !== false){
28556         el.addClass("x-dlg-draggable");
28557         if (!this.proxyDrag) {
28558             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28559         }
28560         else {
28561             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28562         }
28563         dd.setHandleElId(this.header.id);
28564         dd.endDrag = this.endMove.createDelegate(this);
28565         dd.startDrag = this.startMove.createDelegate(this);
28566         dd.onDrag = this.onDrag.createDelegate(this);
28567         dd.scroll = false;
28568         this.dd = dd;
28569     }
28570     if(this.modal){
28571         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28572         this.mask.enableDisplayMode("block");
28573         this.mask.hide();
28574         this.el.addClass("x-dlg-modal");
28575     }
28576     if(this.shadow){
28577         this.shadow = new Roo.Shadow({
28578             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28579             offset : this.shadowOffset
28580         });
28581     }else{
28582         this.shadowOffset = 0;
28583     }
28584     if(Roo.useShims && this.shim !== false){
28585         this.shim = this.el.createShim();
28586         this.shim.hide = this.hideAction;
28587         this.shim.hide();
28588     }else{
28589         this.shim = false;
28590     }
28591     if(this.autoTabs){
28592         this.initTabs();
28593     }
28594     if (this.buttons) { 
28595         var bts= this.buttons;
28596         this.buttons = [];
28597         Roo.each(bts, function(b) {
28598             this.addButton(b);
28599         }, this);
28600     }
28601     
28602     
28603     this.addEvents({
28604         /**
28605          * @event keydown
28606          * Fires when a key is pressed
28607          * @param {Roo.BasicDialog} this
28608          * @param {Roo.EventObject} e
28609          */
28610         "keydown" : true,
28611         /**
28612          * @event move
28613          * Fires when this dialog is moved by the user.
28614          * @param {Roo.BasicDialog} this
28615          * @param {Number} x The new page X
28616          * @param {Number} y The new page Y
28617          */
28618         "move" : true,
28619         /**
28620          * @event resize
28621          * Fires when this dialog is resized by the user.
28622          * @param {Roo.BasicDialog} this
28623          * @param {Number} width The new width
28624          * @param {Number} height The new height
28625          */
28626         "resize" : true,
28627         /**
28628          * @event beforehide
28629          * Fires before this dialog is hidden.
28630          * @param {Roo.BasicDialog} this
28631          */
28632         "beforehide" : true,
28633         /**
28634          * @event hide
28635          * Fires when this dialog is hidden.
28636          * @param {Roo.BasicDialog} this
28637          */
28638         "hide" : true,
28639         /**
28640          * @event beforeshow
28641          * Fires before this dialog is shown.
28642          * @param {Roo.BasicDialog} this
28643          */
28644         "beforeshow" : true,
28645         /**
28646          * @event show
28647          * Fires when this dialog is shown.
28648          * @param {Roo.BasicDialog} this
28649          */
28650         "show" : true
28651     });
28652     el.on("keydown", this.onKeyDown, this);
28653     el.on("mousedown", this.toFront, this);
28654     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28655     this.el.hide();
28656     Roo.DialogManager.register(this);
28657     Roo.BasicDialog.superclass.constructor.call(this);
28658 };
28659
28660 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28661     shadowOffset: Roo.isIE ? 6 : 5,
28662     minHeight: 80,
28663     minWidth: 200,
28664     minButtonWidth: 75,
28665     defaultButton: null,
28666     buttonAlign: "right",
28667     tabTag: 'div',
28668     firstShow: true,
28669
28670     /**
28671      * Sets the dialog title text
28672      * @param {String} text The title text to display
28673      * @return {Roo.BasicDialog} this
28674      */
28675     setTitle : function(text){
28676         this.header.update(text);
28677         return this;
28678     },
28679
28680     // private
28681     closeClick : function(){
28682         this.hide();
28683     },
28684
28685     // private
28686     collapseClick : function(){
28687         this[this.collapsed ? "expand" : "collapse"]();
28688     },
28689
28690     /**
28691      * Collapses the dialog to its minimized state (only the title bar is visible).
28692      * Equivalent to the user clicking the collapse dialog button.
28693      */
28694     collapse : function(){
28695         if(!this.collapsed){
28696             this.collapsed = true;
28697             this.el.addClass("x-dlg-collapsed");
28698             this.restoreHeight = this.el.getHeight();
28699             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28700         }
28701     },
28702
28703     /**
28704      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28705      * clicking the expand dialog button.
28706      */
28707     expand : function(){
28708         if(this.collapsed){
28709             this.collapsed = false;
28710             this.el.removeClass("x-dlg-collapsed");
28711             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28712         }
28713     },
28714
28715     /**
28716      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28717      * @return {Roo.TabPanel} The tabs component
28718      */
28719     initTabs : function(){
28720         var tabs = this.getTabs();
28721         while(tabs.getTab(0)){
28722             tabs.removeTab(0);
28723         }
28724         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28725             var dom = el.dom;
28726             tabs.addTab(Roo.id(dom), dom.title);
28727             dom.title = "";
28728         });
28729         tabs.activate(0);
28730         return tabs;
28731     },
28732
28733     // private
28734     beforeResize : function(){
28735         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28736     },
28737
28738     // private
28739     onResize : function(){
28740         this.refreshSize();
28741         this.syncBodyHeight();
28742         this.adjustAssets();
28743         this.focus();
28744         this.fireEvent("resize", this, this.size.width, this.size.height);
28745     },
28746
28747     // private
28748     onKeyDown : function(e){
28749         if(this.isVisible()){
28750             this.fireEvent("keydown", this, e);
28751         }
28752     },
28753
28754     /**
28755      * Resizes the dialog.
28756      * @param {Number} width
28757      * @param {Number} height
28758      * @return {Roo.BasicDialog} this
28759      */
28760     resizeTo : function(width, height){
28761         this.el.setSize(width, height);
28762         this.size = {width: width, height: height};
28763         this.syncBodyHeight();
28764         if(this.fixedcenter){
28765             this.center();
28766         }
28767         if(this.isVisible()){
28768             this.constrainXY();
28769             this.adjustAssets();
28770         }
28771         this.fireEvent("resize", this, width, height);
28772         return this;
28773     },
28774
28775
28776     /**
28777      * Resizes the dialog to fit the specified content size.
28778      * @param {Number} width
28779      * @param {Number} height
28780      * @return {Roo.BasicDialog} this
28781      */
28782     setContentSize : function(w, h){
28783         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28784         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28785         //if(!this.el.isBorderBox()){
28786             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28787             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28788         //}
28789         if(this.tabs){
28790             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28791             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28792         }
28793         this.resizeTo(w, h);
28794         return this;
28795     },
28796
28797     /**
28798      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28799      * executed in response to a particular key being pressed while the dialog is active.
28800      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28801      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28802      * @param {Function} fn The function to call
28803      * @param {Object} scope (optional) The scope of the function
28804      * @return {Roo.BasicDialog} this
28805      */
28806     addKeyListener : function(key, fn, scope){
28807         var keyCode, shift, ctrl, alt;
28808         if(typeof key == "object" && !(key instanceof Array)){
28809             keyCode = key["key"];
28810             shift = key["shift"];
28811             ctrl = key["ctrl"];
28812             alt = key["alt"];
28813         }else{
28814             keyCode = key;
28815         }
28816         var handler = function(dlg, e){
28817             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28818                 var k = e.getKey();
28819                 if(keyCode instanceof Array){
28820                     for(var i = 0, len = keyCode.length; i < len; i++){
28821                         if(keyCode[i] == k){
28822                           fn.call(scope || window, dlg, k, e);
28823                           return;
28824                         }
28825                     }
28826                 }else{
28827                     if(k == keyCode){
28828                         fn.call(scope || window, dlg, k, e);
28829                     }
28830                 }
28831             }
28832         };
28833         this.on("keydown", handler);
28834         return this;
28835     },
28836
28837     /**
28838      * Returns the TabPanel component (creates it if it doesn't exist).
28839      * Note: If you wish to simply check for the existence of tabs without creating them,
28840      * check for a null 'tabs' property.
28841      * @return {Roo.TabPanel} The tabs component
28842      */
28843     getTabs : function(){
28844         if(!this.tabs){
28845             this.el.addClass("x-dlg-auto-tabs");
28846             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28847             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28848         }
28849         return this.tabs;
28850     },
28851
28852     /**
28853      * Adds a button to the footer section of the dialog.
28854      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28855      * object or a valid Roo.DomHelper element config
28856      * @param {Function} handler The function called when the button is clicked
28857      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28858      * @return {Roo.Button} The new button
28859      */
28860     addButton : function(config, handler, scope){
28861         var dh = Roo.DomHelper;
28862         if(!this.footer){
28863             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28864         }
28865         if(!this.btnContainer){
28866             var tb = this.footer.createChild({
28867
28868                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28869                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28870             }, null, true);
28871             this.btnContainer = tb.firstChild.firstChild.firstChild;
28872         }
28873         var bconfig = {
28874             handler: handler,
28875             scope: scope,
28876             minWidth: this.minButtonWidth,
28877             hideParent:true
28878         };
28879         if(typeof config == "string"){
28880             bconfig.text = config;
28881         }else{
28882             if(config.tag){
28883                 bconfig.dhconfig = config;
28884             }else{
28885                 Roo.apply(bconfig, config);
28886             }
28887         }
28888         var fc = false;
28889         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28890             bconfig.position = Math.max(0, bconfig.position);
28891             fc = this.btnContainer.childNodes[bconfig.position];
28892         }
28893          
28894         var btn = new Roo.Button(
28895             fc ? 
28896                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28897                 : this.btnContainer.appendChild(document.createElement("td")),
28898             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28899             bconfig
28900         );
28901         this.syncBodyHeight();
28902         if(!this.buttons){
28903             /**
28904              * Array of all the buttons that have been added to this dialog via addButton
28905              * @type Array
28906              */
28907             this.buttons = [];
28908         }
28909         this.buttons.push(btn);
28910         return btn;
28911     },
28912
28913     /**
28914      * Sets the default button to be focused when the dialog is displayed.
28915      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28916      * @return {Roo.BasicDialog} this
28917      */
28918     setDefaultButton : function(btn){
28919         this.defaultButton = btn;
28920         return this;
28921     },
28922
28923     // private
28924     getHeaderFooterHeight : function(safe){
28925         var height = 0;
28926         if(this.header){
28927            height += this.header.getHeight();
28928         }
28929         if(this.footer){
28930            var fm = this.footer.getMargins();
28931             height += (this.footer.getHeight()+fm.top+fm.bottom);
28932         }
28933         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28934         height += this.centerBg.getPadding("tb");
28935         return height;
28936     },
28937
28938     // private
28939     syncBodyHeight : function(){
28940         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28941         var height = this.size.height - this.getHeaderFooterHeight(false);
28942         bd.setHeight(height-bd.getMargins("tb"));
28943         var hh = this.header.getHeight();
28944         var h = this.size.height-hh;
28945         cb.setHeight(h);
28946         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28947         bw.setHeight(h-cb.getPadding("tb"));
28948         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28949         bd.setWidth(bw.getWidth(true));
28950         if(this.tabs){
28951             this.tabs.syncHeight();
28952             if(Roo.isIE){
28953                 this.tabs.el.repaint();
28954             }
28955         }
28956     },
28957
28958     /**
28959      * Restores the previous state of the dialog if Roo.state is configured.
28960      * @return {Roo.BasicDialog} this
28961      */
28962     restoreState : function(){
28963         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28964         if(box && box.width){
28965             this.xy = [box.x, box.y];
28966             this.resizeTo(box.width, box.height);
28967         }
28968         return this;
28969     },
28970
28971     // private
28972     beforeShow : function(){
28973         this.expand();
28974         if(this.fixedcenter){
28975             this.xy = this.el.getCenterXY(true);
28976         }
28977         if(this.modal){
28978             Roo.get(document.body).addClass("x-body-masked");
28979             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28980             this.mask.show();
28981         }
28982         this.constrainXY();
28983     },
28984
28985     // private
28986     animShow : function(){
28987         var b = Roo.get(this.animateTarget).getBox();
28988         this.proxy.setSize(b.width, b.height);
28989         this.proxy.setLocation(b.x, b.y);
28990         this.proxy.show();
28991         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28992                     true, .35, this.showEl.createDelegate(this));
28993     },
28994
28995     /**
28996      * Shows the dialog.
28997      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28998      * @return {Roo.BasicDialog} this
28999      */
29000     show : function(animateTarget){
29001         if (this.fireEvent("beforeshow", this) === false){
29002             return;
29003         }
29004         if(this.syncHeightBeforeShow){
29005             this.syncBodyHeight();
29006         }else if(this.firstShow){
29007             this.firstShow = false;
29008             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29009         }
29010         this.animateTarget = animateTarget || this.animateTarget;
29011         if(!this.el.isVisible()){
29012             this.beforeShow();
29013             if(this.animateTarget && Roo.get(this.animateTarget)){
29014                 this.animShow();
29015             }else{
29016                 this.showEl();
29017             }
29018         }
29019         return this;
29020     },
29021
29022     // private
29023     showEl : function(){
29024         this.proxy.hide();
29025         this.el.setXY(this.xy);
29026         this.el.show();
29027         this.adjustAssets(true);
29028         this.toFront();
29029         this.focus();
29030         // IE peekaboo bug - fix found by Dave Fenwick
29031         if(Roo.isIE){
29032             this.el.repaint();
29033         }
29034         this.fireEvent("show", this);
29035     },
29036
29037     /**
29038      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29039      * dialog itself will receive focus.
29040      */
29041     focus : function(){
29042         if(this.defaultButton){
29043             this.defaultButton.focus();
29044         }else{
29045             this.focusEl.focus();
29046         }
29047     },
29048
29049     // private
29050     constrainXY : function(){
29051         if(this.constraintoviewport !== false){
29052             if(!this.viewSize){
29053                 if(this.container){
29054                     var s = this.container.getSize();
29055                     this.viewSize = [s.width, s.height];
29056                 }else{
29057                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29058                 }
29059             }
29060             var s = Roo.get(this.container||document).getScroll();
29061
29062             var x = this.xy[0], y = this.xy[1];
29063             var w = this.size.width, h = this.size.height;
29064             var vw = this.viewSize[0], vh = this.viewSize[1];
29065             // only move it if it needs it
29066             var moved = false;
29067             // first validate right/bottom
29068             if(x + w > vw+s.left){
29069                 x = vw - w;
29070                 moved = true;
29071             }
29072             if(y + h > vh+s.top){
29073                 y = vh - h;
29074                 moved = true;
29075             }
29076             // then make sure top/left isn't negative
29077             if(x < s.left){
29078                 x = s.left;
29079                 moved = true;
29080             }
29081             if(y < s.top){
29082                 y = s.top;
29083                 moved = true;
29084             }
29085             if(moved){
29086                 // cache xy
29087                 this.xy = [x, y];
29088                 if(this.isVisible()){
29089                     this.el.setLocation(x, y);
29090                     this.adjustAssets();
29091                 }
29092             }
29093         }
29094     },
29095
29096     // private
29097     onDrag : function(){
29098         if(!this.proxyDrag){
29099             this.xy = this.el.getXY();
29100             this.adjustAssets();
29101         }
29102     },
29103
29104     // private
29105     adjustAssets : function(doShow){
29106         var x = this.xy[0], y = this.xy[1];
29107         var w = this.size.width, h = this.size.height;
29108         if(doShow === true){
29109             if(this.shadow){
29110                 this.shadow.show(this.el);
29111             }
29112             if(this.shim){
29113                 this.shim.show();
29114             }
29115         }
29116         if(this.shadow && this.shadow.isVisible()){
29117             this.shadow.show(this.el);
29118         }
29119         if(this.shim && this.shim.isVisible()){
29120             this.shim.setBounds(x, y, w, h);
29121         }
29122     },
29123
29124     // private
29125     adjustViewport : function(w, h){
29126         if(!w || !h){
29127             w = Roo.lib.Dom.getViewWidth();
29128             h = Roo.lib.Dom.getViewHeight();
29129         }
29130         // cache the size
29131         this.viewSize = [w, h];
29132         if(this.modal && this.mask.isVisible()){
29133             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29134             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29135         }
29136         if(this.isVisible()){
29137             this.constrainXY();
29138         }
29139     },
29140
29141     /**
29142      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29143      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29144      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29145      */
29146     destroy : function(removeEl){
29147         if(this.isVisible()){
29148             this.animateTarget = null;
29149             this.hide();
29150         }
29151         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29152         if(this.tabs){
29153             this.tabs.destroy(removeEl);
29154         }
29155         Roo.destroy(
29156              this.shim,
29157              this.proxy,
29158              this.resizer,
29159              this.close,
29160              this.mask
29161         );
29162         if(this.dd){
29163             this.dd.unreg();
29164         }
29165         if(this.buttons){
29166            for(var i = 0, len = this.buttons.length; i < len; i++){
29167                this.buttons[i].destroy();
29168            }
29169         }
29170         this.el.removeAllListeners();
29171         if(removeEl === true){
29172             this.el.update("");
29173             this.el.remove();
29174         }
29175         Roo.DialogManager.unregister(this);
29176     },
29177
29178     // private
29179     startMove : function(){
29180         if(this.proxyDrag){
29181             this.proxy.show();
29182         }
29183         if(this.constraintoviewport !== false){
29184             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29185         }
29186     },
29187
29188     // private
29189     endMove : function(){
29190         if(!this.proxyDrag){
29191             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29192         }else{
29193             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29194             this.proxy.hide();
29195         }
29196         this.refreshSize();
29197         this.adjustAssets();
29198         this.focus();
29199         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29200     },
29201
29202     /**
29203      * Brings this dialog to the front of any other visible dialogs
29204      * @return {Roo.BasicDialog} this
29205      */
29206     toFront : function(){
29207         Roo.DialogManager.bringToFront(this);
29208         return this;
29209     },
29210
29211     /**
29212      * Sends this dialog to the back (under) of any other visible dialogs
29213      * @return {Roo.BasicDialog} this
29214      */
29215     toBack : function(){
29216         Roo.DialogManager.sendToBack(this);
29217         return this;
29218     },
29219
29220     /**
29221      * Centers this dialog in the viewport
29222      * @return {Roo.BasicDialog} this
29223      */
29224     center : function(){
29225         var xy = this.el.getCenterXY(true);
29226         this.moveTo(xy[0], xy[1]);
29227         return this;
29228     },
29229
29230     /**
29231      * Moves the dialog's top-left corner to the specified point
29232      * @param {Number} x
29233      * @param {Number} y
29234      * @return {Roo.BasicDialog} this
29235      */
29236     moveTo : function(x, y){
29237         this.xy = [x,y];
29238         if(this.isVisible()){
29239             this.el.setXY(this.xy);
29240             this.adjustAssets();
29241         }
29242         return this;
29243     },
29244
29245     /**
29246      * Aligns the dialog to the specified element
29247      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29248      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29249      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29250      * @return {Roo.BasicDialog} this
29251      */
29252     alignTo : function(element, position, offsets){
29253         this.xy = this.el.getAlignToXY(element, position, offsets);
29254         if(this.isVisible()){
29255             this.el.setXY(this.xy);
29256             this.adjustAssets();
29257         }
29258         return this;
29259     },
29260
29261     /**
29262      * Anchors an element to another element and realigns it when the window is resized.
29263      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29264      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29265      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29266      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29267      * is a number, it is used as the buffer delay (defaults to 50ms).
29268      * @return {Roo.BasicDialog} this
29269      */
29270     anchorTo : function(el, alignment, offsets, monitorScroll){
29271         var action = function(){
29272             this.alignTo(el, alignment, offsets);
29273         };
29274         Roo.EventManager.onWindowResize(action, this);
29275         var tm = typeof monitorScroll;
29276         if(tm != 'undefined'){
29277             Roo.EventManager.on(window, 'scroll', action, this,
29278                 {buffer: tm == 'number' ? monitorScroll : 50});
29279         }
29280         action.call(this);
29281         return this;
29282     },
29283
29284     /**
29285      * Returns true if the dialog is visible
29286      * @return {Boolean}
29287      */
29288     isVisible : function(){
29289         return this.el.isVisible();
29290     },
29291
29292     // private
29293     animHide : function(callback){
29294         var b = Roo.get(this.animateTarget).getBox();
29295         this.proxy.show();
29296         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29297         this.el.hide();
29298         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29299                     this.hideEl.createDelegate(this, [callback]));
29300     },
29301
29302     /**
29303      * Hides the dialog.
29304      * @param {Function} callback (optional) Function to call when the dialog is hidden
29305      * @return {Roo.BasicDialog} this
29306      */
29307     hide : function(callback){
29308         if (this.fireEvent("beforehide", this) === false){
29309             return;
29310         }
29311         if(this.shadow){
29312             this.shadow.hide();
29313         }
29314         if(this.shim) {
29315           this.shim.hide();
29316         }
29317         // sometimes animateTarget seems to get set.. causing problems...
29318         // this just double checks..
29319         if(this.animateTarget && Roo.get(this.animateTarget)) {
29320            this.animHide(callback);
29321         }else{
29322             this.el.hide();
29323             this.hideEl(callback);
29324         }
29325         return this;
29326     },
29327
29328     // private
29329     hideEl : function(callback){
29330         this.proxy.hide();
29331         if(this.modal){
29332             this.mask.hide();
29333             Roo.get(document.body).removeClass("x-body-masked");
29334         }
29335         this.fireEvent("hide", this);
29336         if(typeof callback == "function"){
29337             callback();
29338         }
29339     },
29340
29341     // private
29342     hideAction : function(){
29343         this.setLeft("-10000px");
29344         this.setTop("-10000px");
29345         this.setStyle("visibility", "hidden");
29346     },
29347
29348     // private
29349     refreshSize : function(){
29350         this.size = this.el.getSize();
29351         this.xy = this.el.getXY();
29352         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29353     },
29354
29355     // private
29356     // z-index is managed by the DialogManager and may be overwritten at any time
29357     setZIndex : function(index){
29358         if(this.modal){
29359             this.mask.setStyle("z-index", index);
29360         }
29361         if(this.shim){
29362             this.shim.setStyle("z-index", ++index);
29363         }
29364         if(this.shadow){
29365             this.shadow.setZIndex(++index);
29366         }
29367         this.el.setStyle("z-index", ++index);
29368         if(this.proxy){
29369             this.proxy.setStyle("z-index", ++index);
29370         }
29371         if(this.resizer){
29372             this.resizer.proxy.setStyle("z-index", ++index);
29373         }
29374
29375         this.lastZIndex = index;
29376     },
29377
29378     /**
29379      * Returns the element for this dialog
29380      * @return {Roo.Element} The underlying dialog Element
29381      */
29382     getEl : function(){
29383         return this.el;
29384     }
29385 });
29386
29387 /**
29388  * @class Roo.DialogManager
29389  * Provides global access to BasicDialogs that have been created and
29390  * support for z-indexing (layering) multiple open dialogs.
29391  */
29392 Roo.DialogManager = function(){
29393     var list = {};
29394     var accessList = [];
29395     var front = null;
29396
29397     // private
29398     var sortDialogs = function(d1, d2){
29399         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29400     };
29401
29402     // private
29403     var orderDialogs = function(){
29404         accessList.sort(sortDialogs);
29405         var seed = Roo.DialogManager.zseed;
29406         for(var i = 0, len = accessList.length; i < len; i++){
29407             var dlg = accessList[i];
29408             if(dlg){
29409                 dlg.setZIndex(seed + (i*10));
29410             }
29411         }
29412     };
29413
29414     return {
29415         /**
29416          * The starting z-index for BasicDialogs (defaults to 9000)
29417          * @type Number The z-index value
29418          */
29419         zseed : 9000,
29420
29421         // private
29422         register : function(dlg){
29423             list[dlg.id] = dlg;
29424             accessList.push(dlg);
29425         },
29426
29427         // private
29428         unregister : function(dlg){
29429             delete list[dlg.id];
29430             var i=0;
29431             var len=0;
29432             if(!accessList.indexOf){
29433                 for(  i = 0, len = accessList.length; i < len; i++){
29434                     if(accessList[i] == dlg){
29435                         accessList.splice(i, 1);
29436                         return;
29437                     }
29438                 }
29439             }else{
29440                  i = accessList.indexOf(dlg);
29441                 if(i != -1){
29442                     accessList.splice(i, 1);
29443                 }
29444             }
29445         },
29446
29447         /**
29448          * Gets a registered dialog by id
29449          * @param {String/Object} id The id of the dialog or a dialog
29450          * @return {Roo.BasicDialog} this
29451          */
29452         get : function(id){
29453             return typeof id == "object" ? id : list[id];
29454         },
29455
29456         /**
29457          * Brings the specified dialog to the front
29458          * @param {String/Object} dlg The id of the dialog or a dialog
29459          * @return {Roo.BasicDialog} this
29460          */
29461         bringToFront : function(dlg){
29462             dlg = this.get(dlg);
29463             if(dlg != front){
29464                 front = dlg;
29465                 dlg._lastAccess = new Date().getTime();
29466                 orderDialogs();
29467             }
29468             return dlg;
29469         },
29470
29471         /**
29472          * Sends the specified dialog to the back
29473          * @param {String/Object} dlg The id of the dialog or a dialog
29474          * @return {Roo.BasicDialog} this
29475          */
29476         sendToBack : function(dlg){
29477             dlg = this.get(dlg);
29478             dlg._lastAccess = -(new Date().getTime());
29479             orderDialogs();
29480             return dlg;
29481         },
29482
29483         /**
29484          * Hides all dialogs
29485          */
29486         hideAll : function(){
29487             for(var id in list){
29488                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29489                     list[id].hide();
29490                 }
29491             }
29492         }
29493     };
29494 }();
29495
29496 /**
29497  * @class Roo.LayoutDialog
29498  * @extends Roo.BasicDialog
29499  * Dialog which provides adjustments for working with a layout in a Dialog.
29500  * Add your necessary layout config options to the dialog's config.<br>
29501  * Example usage (including a nested layout):
29502  * <pre><code>
29503 if(!dialog){
29504     dialog = new Roo.LayoutDialog("download-dlg", {
29505         modal: true,
29506         width:600,
29507         height:450,
29508         shadow:true,
29509         minWidth:500,
29510         minHeight:350,
29511         autoTabs:true,
29512         proxyDrag:true,
29513         // layout config merges with the dialog config
29514         center:{
29515             tabPosition: "top",
29516             alwaysShowTabs: true
29517         }
29518     });
29519     dialog.addKeyListener(27, dialog.hide, dialog);
29520     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29521     dialog.addButton("Build It!", this.getDownload, this);
29522
29523     // we can even add nested layouts
29524     var innerLayout = new Roo.BorderLayout("dl-inner", {
29525         east: {
29526             initialSize: 200,
29527             autoScroll:true,
29528             split:true
29529         },
29530         center: {
29531             autoScroll:true
29532         }
29533     });
29534     innerLayout.beginUpdate();
29535     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29536     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29537     innerLayout.endUpdate(true);
29538
29539     var layout = dialog.getLayout();
29540     layout.beginUpdate();
29541     layout.add("center", new Roo.ContentPanel("standard-panel",
29542                         {title: "Download the Source", fitToFrame:true}));
29543     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29544                {title: "Build your own roo.js"}));
29545     layout.getRegion("center").showPanel(sp);
29546     layout.endUpdate();
29547 }
29548 </code></pre>
29549     * @constructor
29550     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29551     * @param {Object} config configuration options
29552   */
29553 Roo.LayoutDialog = function(el, cfg){
29554     
29555     var config=  cfg;
29556     if (typeof(cfg) == 'undefined') {
29557         config = Roo.apply({}, el);
29558         // not sure why we use documentElement here.. - it should always be body.
29559         // IE7 borks horribly if we use documentElement.
29560         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
29561         //config.autoCreate = true;
29562     }
29563     
29564     
29565     config.autoTabs = false;
29566     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29567     this.body.setStyle({overflow:"hidden", position:"relative"});
29568     this.layout = new Roo.BorderLayout(this.body.dom, config);
29569     this.layout.monitorWindowResize = false;
29570     this.el.addClass("x-dlg-auto-layout");
29571     // fix case when center region overwrites center function
29572     this.center = Roo.BasicDialog.prototype.center;
29573     this.on("show", this.layout.layout, this.layout, true);
29574     if (config.items) {
29575         var xitems = config.items;
29576         delete config.items;
29577         Roo.each(xitems, this.addxtype, this);
29578     }
29579     
29580     
29581 };
29582 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29583     /**
29584      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29585      * @deprecated
29586      */
29587     endUpdate : function(){
29588         this.layout.endUpdate();
29589     },
29590
29591     /**
29592      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29593      *  @deprecated
29594      */
29595     beginUpdate : function(){
29596         this.layout.beginUpdate();
29597     },
29598
29599     /**
29600      * Get the BorderLayout for this dialog
29601      * @return {Roo.BorderLayout}
29602      */
29603     getLayout : function(){
29604         return this.layout;
29605     },
29606
29607     showEl : function(){
29608         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29609         if(Roo.isIE7){
29610             this.layout.layout();
29611         }
29612     },
29613
29614     // private
29615     // Use the syncHeightBeforeShow config option to control this automatically
29616     syncBodyHeight : function(){
29617         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29618         if(this.layout){this.layout.layout();}
29619     },
29620     
29621       /**
29622      * Add an xtype element (actually adds to the layout.)
29623      * @return {Object} xdata xtype object data.
29624      */
29625     
29626     addxtype : function(c) {
29627         return this.layout.addxtype(c);
29628     }
29629 });/*
29630  * Based on:
29631  * Ext JS Library 1.1.1
29632  * Copyright(c) 2006-2007, Ext JS, LLC.
29633  *
29634  * Originally Released Under LGPL - original licence link has changed is not relivant.
29635  *
29636  * Fork - LGPL
29637  * <script type="text/javascript">
29638  */
29639  
29640 /**
29641  * @class Roo.MessageBox
29642  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29643  * Example usage:
29644  *<pre><code>
29645 // Basic alert:
29646 Roo.Msg.alert('Status', 'Changes saved successfully.');
29647
29648 // Prompt for user data:
29649 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29650     if (btn == 'ok'){
29651         // process text value...
29652     }
29653 });
29654
29655 // Show a dialog using config options:
29656 Roo.Msg.show({
29657    title:'Save Changes?',
29658    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29659    buttons: Roo.Msg.YESNOCANCEL,
29660    fn: processResult,
29661    animEl: 'elId'
29662 });
29663 </code></pre>
29664  * @singleton
29665  */
29666 Roo.MessageBox = function(){
29667     var dlg, opt, mask, waitTimer;
29668     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29669     var buttons, activeTextEl, bwidth;
29670
29671     // private
29672     var handleButton = function(button){
29673         dlg.hide();
29674         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29675     };
29676
29677     // private
29678     var handleHide = function(){
29679         if(opt && opt.cls){
29680             dlg.el.removeClass(opt.cls);
29681         }
29682         if(waitTimer){
29683             Roo.TaskMgr.stop(waitTimer);
29684             waitTimer = null;
29685         }
29686     };
29687
29688     // private
29689     var updateButtons = function(b){
29690         var width = 0;
29691         if(!b){
29692             buttons["ok"].hide();
29693             buttons["cancel"].hide();
29694             buttons["yes"].hide();
29695             buttons["no"].hide();
29696             dlg.footer.dom.style.display = 'none';
29697             return width;
29698         }
29699         dlg.footer.dom.style.display = '';
29700         for(var k in buttons){
29701             if(typeof buttons[k] != "function"){
29702                 if(b[k]){
29703                     buttons[k].show();
29704                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29705                     width += buttons[k].el.getWidth()+15;
29706                 }else{
29707                     buttons[k].hide();
29708                 }
29709             }
29710         }
29711         return width;
29712     };
29713
29714     // private
29715     var handleEsc = function(d, k, e){
29716         if(opt && opt.closable !== false){
29717             dlg.hide();
29718         }
29719         if(e){
29720             e.stopEvent();
29721         }
29722     };
29723
29724     return {
29725         /**
29726          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29727          * @return {Roo.BasicDialog} The BasicDialog element
29728          */
29729         getDialog : function(){
29730            if(!dlg){
29731                 dlg = new Roo.BasicDialog("x-msg-box", {
29732                     autoCreate : true,
29733                     shadow: true,
29734                     draggable: true,
29735                     resizable:false,
29736                     constraintoviewport:false,
29737                     fixedcenter:true,
29738                     collapsible : false,
29739                     shim:true,
29740                     modal: true,
29741                     width:400, height:100,
29742                     buttonAlign:"center",
29743                     closeClick : function(){
29744                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29745                             handleButton("no");
29746                         }else{
29747                             handleButton("cancel");
29748                         }
29749                     }
29750                 });
29751                 dlg.on("hide", handleHide);
29752                 mask = dlg.mask;
29753                 dlg.addKeyListener(27, handleEsc);
29754                 buttons = {};
29755                 var bt = this.buttonText;
29756                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29757                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29758                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29759                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29760                 bodyEl = dlg.body.createChild({
29761
29762                     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>'
29763                 });
29764                 msgEl = bodyEl.dom.firstChild;
29765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29766                 textboxEl.enableDisplayMode();
29767                 textboxEl.addKeyListener([10,13], function(){
29768                     if(dlg.isVisible() && opt && opt.buttons){
29769                         if(opt.buttons.ok){
29770                             handleButton("ok");
29771                         }else if(opt.buttons.yes){
29772                             handleButton("yes");
29773                         }
29774                     }
29775                 });
29776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29777                 textareaEl.enableDisplayMode();
29778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29779                 progressEl.enableDisplayMode();
29780                 var pf = progressEl.dom.firstChild;
29781                 if (pf) {
29782                     pp = Roo.get(pf.firstChild);
29783                     pp.setHeight(pf.offsetHeight);
29784                 }
29785                 
29786             }
29787             return dlg;
29788         },
29789
29790         /**
29791          * Updates the message box body text
29792          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29793          * the XHTML-compliant non-breaking space character '&amp;#160;')
29794          * @return {Roo.MessageBox} This message box
29795          */
29796         updateText : function(text){
29797             if(!dlg.isVisible() && !opt.width){
29798                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29799             }
29800             msgEl.innerHTML = text || '&#160;';
29801             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29802                         Math.max(opt.minWidth || this.minWidth, bwidth));
29803             if(opt.prompt){
29804                 activeTextEl.setWidth(w);
29805             }
29806             if(dlg.isVisible()){
29807                 dlg.fixedcenter = false;
29808             }
29809             dlg.setContentSize(w, bodyEl.getHeight());
29810             if(dlg.isVisible()){
29811                 dlg.fixedcenter = true;
29812             }
29813             return this;
29814         },
29815
29816         /**
29817          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29818          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29819          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29820          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29821          * @return {Roo.MessageBox} This message box
29822          */
29823         updateProgress : function(value, text){
29824             if(text){
29825                 this.updateText(text);
29826             }
29827             if (pp) { // weird bug on my firefox - for some reason this is not defined
29828                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29829             }
29830             return this;
29831         },        
29832
29833         /**
29834          * Returns true if the message box is currently displayed
29835          * @return {Boolean} True if the message box is visible, else false
29836          */
29837         isVisible : function(){
29838             return dlg && dlg.isVisible();  
29839         },
29840
29841         /**
29842          * Hides the message box if it is displayed
29843          */
29844         hide : function(){
29845             if(this.isVisible()){
29846                 dlg.hide();
29847             }  
29848         },
29849
29850         /**
29851          * Displays a new message box, or reinitializes an existing message box, based on the config options
29852          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29853          * The following config object properties are supported:
29854          * <pre>
29855 Property    Type             Description
29856 ----------  ---------------  ------------------------------------------------------------------------------------
29857 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29858                                    closes (defaults to undefined)
29859 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29860                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29861 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29862                                    progress and wait dialogs will ignore this property and always hide the
29863                                    close button as they can only be closed programmatically.
29864 cls               String           A custom CSS class to apply to the message box element
29865 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29866                                    displayed (defaults to 75)
29867 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29868                                    function will be btn (the name of the button that was clicked, if applicable,
29869                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29870                                    Progress and wait dialogs will ignore this option since they do not respond to
29871                                    user actions and can only be closed programmatically, so any required function
29872                                    should be called by the same code after it closes the dialog.
29873 icon              String           A CSS class that provides a background image to be used as an icon for
29874                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29875 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29876 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29877 modal             Boolean          False to allow user interaction with the page while the message box is
29878                                    displayed (defaults to true)
29879 msg               String           A string that will replace the existing message box body text (defaults
29880                                    to the XHTML-compliant non-breaking space character '&#160;')
29881 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29882 progress          Boolean          True to display a progress bar (defaults to false)
29883 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29884 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29885 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29886 title             String           The title text
29887 value             String           The string value to set into the active textbox element if displayed
29888 wait              Boolean          True to display a progress bar (defaults to false)
29889 width             Number           The width of the dialog in pixels
29890 </pre>
29891          *
29892          * Example usage:
29893          * <pre><code>
29894 Roo.Msg.show({
29895    title: 'Address',
29896    msg: 'Please enter your address:',
29897    width: 300,
29898    buttons: Roo.MessageBox.OKCANCEL,
29899    multiline: true,
29900    fn: saveAddress,
29901    animEl: 'addAddressBtn'
29902 });
29903 </code></pre>
29904          * @param {Object} config Configuration options
29905          * @return {Roo.MessageBox} This message box
29906          */
29907         show : function(options){
29908             if(this.isVisible()){
29909                 this.hide();
29910             }
29911             var d = this.getDialog();
29912             opt = options;
29913             d.setTitle(opt.title || "&#160;");
29914             d.close.setDisplayed(opt.closable !== false);
29915             activeTextEl = textboxEl;
29916             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29917             if(opt.prompt){
29918                 if(opt.multiline){
29919                     textboxEl.hide();
29920                     textareaEl.show();
29921                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29922                         opt.multiline : this.defaultTextHeight);
29923                     activeTextEl = textareaEl;
29924                 }else{
29925                     textboxEl.show();
29926                     textareaEl.hide();
29927                 }
29928             }else{
29929                 textboxEl.hide();
29930                 textareaEl.hide();
29931             }
29932             progressEl.setDisplayed(opt.progress === true);
29933             this.updateProgress(0);
29934             activeTextEl.dom.value = opt.value || "";
29935             if(opt.prompt){
29936                 dlg.setDefaultButton(activeTextEl);
29937             }else{
29938                 var bs = opt.buttons;
29939                 var db = null;
29940                 if(bs && bs.ok){
29941                     db = buttons["ok"];
29942                 }else if(bs && bs.yes){
29943                     db = buttons["yes"];
29944                 }
29945                 dlg.setDefaultButton(db);
29946             }
29947             bwidth = updateButtons(opt.buttons);
29948             this.updateText(opt.msg);
29949             if(opt.cls){
29950                 d.el.addClass(opt.cls);
29951             }
29952             d.proxyDrag = opt.proxyDrag === true;
29953             d.modal = opt.modal !== false;
29954             d.mask = opt.modal !== false ? mask : false;
29955             if(!d.isVisible()){
29956                 // force it to the end of the z-index stack so it gets a cursor in FF
29957                 document.body.appendChild(dlg.el.dom);
29958                 d.animateTarget = null;
29959                 d.show(options.animEl);
29960             }
29961             return this;
29962         },
29963
29964         /**
29965          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29966          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29967          * and closing the message box when the process is complete.
29968          * @param {String} title The title bar text
29969          * @param {String} msg The message box body text
29970          * @return {Roo.MessageBox} This message box
29971          */
29972         progress : function(title, msg){
29973             this.show({
29974                 title : title,
29975                 msg : msg,
29976                 buttons: false,
29977                 progress:true,
29978                 closable:false,
29979                 minWidth: this.minProgressWidth,
29980                 modal : true
29981             });
29982             return this;
29983         },
29984
29985         /**
29986          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29987          * If a callback function is passed it will be called after the user clicks the button, and the
29988          * id of the button that was clicked will be passed as the only parameter to the callback
29989          * (could also be the top-right close button).
29990          * @param {String} title The title bar text
29991          * @param {String} msg The message box body text
29992          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29993          * @param {Object} scope (optional) The scope of the callback function
29994          * @return {Roo.MessageBox} This message box
29995          */
29996         alert : function(title, msg, fn, scope){
29997             this.show({
29998                 title : title,
29999                 msg : msg,
30000                 buttons: this.OK,
30001                 fn: fn,
30002                 scope : scope,
30003                 modal : true
30004             });
30005             return this;
30006         },
30007
30008         /**
30009          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30010          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30011          * You are responsible for closing the message box when the process is complete.
30012          * @param {String} msg The message box body text
30013          * @param {String} title (optional) The title bar text
30014          * @return {Roo.MessageBox} This message box
30015          */
30016         wait : function(msg, title){
30017             this.show({
30018                 title : title,
30019                 msg : msg,
30020                 buttons: false,
30021                 closable:false,
30022                 progress:true,
30023                 modal:true,
30024                 width:300,
30025                 wait:true
30026             });
30027             waitTimer = Roo.TaskMgr.start({
30028                 run: function(i){
30029                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30030                 },
30031                 interval: 1000
30032             });
30033             return this;
30034         },
30035
30036         /**
30037          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30038          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30039          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30040          * @param {String} title The title bar text
30041          * @param {String} msg The message box body text
30042          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30043          * @param {Object} scope (optional) The scope of the callback function
30044          * @return {Roo.MessageBox} This message box
30045          */
30046         confirm : function(title, msg, fn, scope){
30047             this.show({
30048                 title : title,
30049                 msg : msg,
30050                 buttons: this.YESNO,
30051                 fn: fn,
30052                 scope : scope,
30053                 modal : true
30054             });
30055             return this;
30056         },
30057
30058         /**
30059          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30060          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30061          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30062          * (could also be the top-right close button) and the text that was entered will be passed as the two
30063          * parameters to the callback.
30064          * @param {String} title The title bar text
30065          * @param {String} msg The message box body text
30066          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30067          * @param {Object} scope (optional) The scope of the callback function
30068          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30069          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30070          * @return {Roo.MessageBox} This message box
30071          */
30072         prompt : function(title, msg, fn, scope, multiline){
30073             this.show({
30074                 title : title,
30075                 msg : msg,
30076                 buttons: this.OKCANCEL,
30077                 fn: fn,
30078                 minWidth:250,
30079                 scope : scope,
30080                 prompt:true,
30081                 multiline: multiline,
30082                 modal : true
30083             });
30084             return this;
30085         },
30086
30087         /**
30088          * Button config that displays a single OK button
30089          * @type Object
30090          */
30091         OK : {ok:true},
30092         /**
30093          * Button config that displays Yes and No buttons
30094          * @type Object
30095          */
30096         YESNO : {yes:true, no:true},
30097         /**
30098          * Button config that displays OK and Cancel buttons
30099          * @type Object
30100          */
30101         OKCANCEL : {ok:true, cancel:true},
30102         /**
30103          * Button config that displays Yes, No and Cancel buttons
30104          * @type Object
30105          */
30106         YESNOCANCEL : {yes:true, no:true, cancel:true},
30107
30108         /**
30109          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30110          * @type Number
30111          */
30112         defaultTextHeight : 75,
30113         /**
30114          * The maximum width in pixels of the message box (defaults to 600)
30115          * @type Number
30116          */
30117         maxWidth : 600,
30118         /**
30119          * The minimum width in pixels of the message box (defaults to 100)
30120          * @type Number
30121          */
30122         minWidth : 100,
30123         /**
30124          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30125          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30126          * @type Number
30127          */
30128         minProgressWidth : 250,
30129         /**
30130          * An object containing the default button text strings that can be overriden for localized language support.
30131          * Supported properties are: ok, cancel, yes and no.
30132          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30133          * @type Object
30134          */
30135         buttonText : {
30136             ok : "OK",
30137             cancel : "Cancel",
30138             yes : "Yes",
30139             no : "No"
30140         }
30141     };
30142 }();
30143
30144 /**
30145  * Shorthand for {@link Roo.MessageBox}
30146  */
30147 Roo.Msg = Roo.MessageBox;/*
30148  * Based on:
30149  * Ext JS Library 1.1.1
30150  * Copyright(c) 2006-2007, Ext JS, LLC.
30151  *
30152  * Originally Released Under LGPL - original licence link has changed is not relivant.
30153  *
30154  * Fork - LGPL
30155  * <script type="text/javascript">
30156  */
30157 /**
30158  * @class Roo.QuickTips
30159  * Provides attractive and customizable tooltips for any element.
30160  * @singleton
30161  */
30162 Roo.QuickTips = function(){
30163     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30164     var ce, bd, xy, dd;
30165     var visible = false, disabled = true, inited = false;
30166     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30167     
30168     var onOver = function(e){
30169         if(disabled){
30170             return;
30171         }
30172         var t = e.getTarget();
30173         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30174             return;
30175         }
30176         if(ce && t == ce.el){
30177             clearTimeout(hideProc);
30178             return;
30179         }
30180         if(t && tagEls[t.id]){
30181             tagEls[t.id].el = t;
30182             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30183             return;
30184         }
30185         var ttp, et = Roo.fly(t);
30186         var ns = cfg.namespace;
30187         if(tm.interceptTitles && t.title){
30188             ttp = t.title;
30189             t.qtip = ttp;
30190             t.removeAttribute("title");
30191             e.preventDefault();
30192         }else{
30193             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30194         }
30195         if(ttp){
30196             showProc = show.defer(tm.showDelay, tm, [{
30197                 el: t, 
30198                 text: ttp, 
30199                 width: et.getAttributeNS(ns, cfg.width),
30200                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30201                 title: et.getAttributeNS(ns, cfg.title),
30202                     cls: et.getAttributeNS(ns, cfg.cls)
30203             }]);
30204         }
30205     };
30206     
30207     var onOut = function(e){
30208         clearTimeout(showProc);
30209         var t = e.getTarget();
30210         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30211             hideProc = setTimeout(hide, tm.hideDelay);
30212         }
30213     };
30214     
30215     var onMove = function(e){
30216         if(disabled){
30217             return;
30218         }
30219         xy = e.getXY();
30220         xy[1] += 18;
30221         if(tm.trackMouse && ce){
30222             el.setXY(xy);
30223         }
30224     };
30225     
30226     var onDown = function(e){
30227         clearTimeout(showProc);
30228         clearTimeout(hideProc);
30229         if(!e.within(el)){
30230             if(tm.hideOnClick){
30231                 hide();
30232                 tm.disable();
30233                 tm.enable.defer(100, tm);
30234             }
30235         }
30236     };
30237     
30238     var getPad = function(){
30239         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30240     };
30241
30242     var show = function(o){
30243         if(disabled){
30244             return;
30245         }
30246         clearTimeout(dismissProc);
30247         ce = o;
30248         if(removeCls){ // in case manually hidden
30249             el.removeClass(removeCls);
30250             removeCls = null;
30251         }
30252         if(ce.cls){
30253             el.addClass(ce.cls);
30254             removeCls = ce.cls;
30255         }
30256         if(ce.title){
30257             tipTitle.update(ce.title);
30258             tipTitle.show();
30259         }else{
30260             tipTitle.update('');
30261             tipTitle.hide();
30262         }
30263         el.dom.style.width  = tm.maxWidth+'px';
30264         //tipBody.dom.style.width = '';
30265         tipBodyText.update(o.text);
30266         var p = getPad(), w = ce.width;
30267         if(!w){
30268             var td = tipBodyText.dom;
30269             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30270             if(aw > tm.maxWidth){
30271                 w = tm.maxWidth;
30272             }else if(aw < tm.minWidth){
30273                 w = tm.minWidth;
30274             }else{
30275                 w = aw;
30276             }
30277         }
30278         //tipBody.setWidth(w);
30279         el.setWidth(parseInt(w, 10) + p);
30280         if(ce.autoHide === false){
30281             close.setDisplayed(true);
30282             if(dd){
30283                 dd.unlock();
30284             }
30285         }else{
30286             close.setDisplayed(false);
30287             if(dd){
30288                 dd.lock();
30289             }
30290         }
30291         if(xy){
30292             el.avoidY = xy[1]-18;
30293             el.setXY(xy);
30294         }
30295         if(tm.animate){
30296             el.setOpacity(.1);
30297             el.setStyle("visibility", "visible");
30298             el.fadeIn({callback: afterShow});
30299         }else{
30300             afterShow();
30301         }
30302     };
30303     
30304     var afterShow = function(){
30305         if(ce){
30306             el.show();
30307             esc.enable();
30308             if(tm.autoDismiss && ce.autoHide !== false){
30309                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30310             }
30311         }
30312     };
30313     
30314     var hide = function(noanim){
30315         clearTimeout(dismissProc);
30316         clearTimeout(hideProc);
30317         ce = null;
30318         if(el.isVisible()){
30319             esc.disable();
30320             if(noanim !== true && tm.animate){
30321                 el.fadeOut({callback: afterHide});
30322             }else{
30323                 afterHide();
30324             } 
30325         }
30326     };
30327     
30328     var afterHide = function(){
30329         el.hide();
30330         if(removeCls){
30331             el.removeClass(removeCls);
30332             removeCls = null;
30333         }
30334     };
30335     
30336     return {
30337         /**
30338         * @cfg {Number} minWidth
30339         * The minimum width of the quick tip (defaults to 40)
30340         */
30341        minWidth : 40,
30342         /**
30343         * @cfg {Number} maxWidth
30344         * The maximum width of the quick tip (defaults to 300)
30345         */
30346        maxWidth : 300,
30347         /**
30348         * @cfg {Boolean} interceptTitles
30349         * True to automatically use the element's DOM title value if available (defaults to false)
30350         */
30351        interceptTitles : false,
30352         /**
30353         * @cfg {Boolean} trackMouse
30354         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30355         */
30356        trackMouse : false,
30357         /**
30358         * @cfg {Boolean} hideOnClick
30359         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30360         */
30361        hideOnClick : true,
30362         /**
30363         * @cfg {Number} showDelay
30364         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30365         */
30366        showDelay : 500,
30367         /**
30368         * @cfg {Number} hideDelay
30369         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30370         */
30371        hideDelay : 200,
30372         /**
30373         * @cfg {Boolean} autoHide
30374         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30375         * Used in conjunction with hideDelay.
30376         */
30377        autoHide : true,
30378         /**
30379         * @cfg {Boolean}
30380         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30381         * (defaults to true).  Used in conjunction with autoDismissDelay.
30382         */
30383        autoDismiss : true,
30384         /**
30385         * @cfg {Number}
30386         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30387         */
30388        autoDismissDelay : 5000,
30389        /**
30390         * @cfg {Boolean} animate
30391         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30392         */
30393        animate : false,
30394
30395        /**
30396         * @cfg {String} title
30397         * Title text to display (defaults to '').  This can be any valid HTML markup.
30398         */
30399         title: '',
30400        /**
30401         * @cfg {String} text
30402         * Body text to display (defaults to '').  This can be any valid HTML markup.
30403         */
30404         text : '',
30405        /**
30406         * @cfg {String} cls
30407         * A CSS class to apply to the base quick tip element (defaults to '').
30408         */
30409         cls : '',
30410        /**
30411         * @cfg {Number} width
30412         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30413         * minWidth or maxWidth.
30414         */
30415         width : null,
30416
30417     /**
30418      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30419      * or display QuickTips in a page.
30420      */
30421        init : function(){
30422           tm = Roo.QuickTips;
30423           cfg = tm.tagConfig;
30424           if(!inited){
30425               if(!Roo.isReady){ // allow calling of init() before onReady
30426                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30427                   return;
30428               }
30429               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30430               el.fxDefaults = {stopFx: true};
30431               // maximum custom styling
30432               //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>');
30433               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>');              
30434               tipTitle = el.child('h3');
30435               tipTitle.enableDisplayMode("block");
30436               tipBody = el.child('div.x-tip-bd');
30437               tipBodyText = el.child('div.x-tip-bd-inner');
30438               //bdLeft = el.child('div.x-tip-bd-left');
30439               //bdRight = el.child('div.x-tip-bd-right');
30440               close = el.child('div.x-tip-close');
30441               close.enableDisplayMode("block");
30442               close.on("click", hide);
30443               var d = Roo.get(document);
30444               d.on("mousedown", onDown);
30445               d.on("mouseover", onOver);
30446               d.on("mouseout", onOut);
30447               d.on("mousemove", onMove);
30448               esc = d.addKeyListener(27, hide);
30449               esc.disable();
30450               if(Roo.dd.DD){
30451                   dd = el.initDD("default", null, {
30452                       onDrag : function(){
30453                           el.sync();  
30454                       }
30455                   });
30456                   dd.setHandleElId(tipTitle.id);
30457                   dd.lock();
30458               }
30459               inited = true;
30460           }
30461           this.enable(); 
30462        },
30463
30464     /**
30465      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30466      * are supported:
30467      * <pre>
30468 Property    Type                   Description
30469 ----------  ---------------------  ------------------------------------------------------------------------
30470 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30471      * </ul>
30472      * @param {Object} config The config object
30473      */
30474        register : function(config){
30475            var cs = config instanceof Array ? config : arguments;
30476            for(var i = 0, len = cs.length; i < len; i++) {
30477                var c = cs[i];
30478                var target = c.target;
30479                if(target){
30480                    if(target instanceof Array){
30481                        for(var j = 0, jlen = target.length; j < jlen; j++){
30482                            tagEls[target[j]] = c;
30483                        }
30484                    }else{
30485                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30486                    }
30487                }
30488            }
30489        },
30490
30491     /**
30492      * Removes this quick tip from its element and destroys it.
30493      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30494      */
30495        unregister : function(el){
30496            delete tagEls[Roo.id(el)];
30497        },
30498
30499     /**
30500      * Enable this quick tip.
30501      */
30502        enable : function(){
30503            if(inited && disabled){
30504                locks.pop();
30505                if(locks.length < 1){
30506                    disabled = false;
30507                }
30508            }
30509        },
30510
30511     /**
30512      * Disable this quick tip.
30513      */
30514        disable : function(){
30515           disabled = true;
30516           clearTimeout(showProc);
30517           clearTimeout(hideProc);
30518           clearTimeout(dismissProc);
30519           if(ce){
30520               hide(true);
30521           }
30522           locks.push(1);
30523        },
30524
30525     /**
30526      * Returns true if the quick tip is enabled, else false.
30527      */
30528        isEnabled : function(){
30529             return !disabled;
30530        },
30531
30532         // private
30533        tagConfig : {
30534            namespace : "ext",
30535            attribute : "qtip",
30536            width : "width",
30537            target : "target",
30538            title : "qtitle",
30539            hide : "hide",
30540            cls : "qclass"
30541        }
30542    };
30543 }();
30544
30545 // backwards compat
30546 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30547  * Based on:
30548  * Ext JS Library 1.1.1
30549  * Copyright(c) 2006-2007, Ext JS, LLC.
30550  *
30551  * Originally Released Under LGPL - original licence link has changed is not relivant.
30552  *
30553  * Fork - LGPL
30554  * <script type="text/javascript">
30555  */
30556  
30557
30558 /**
30559  * @class Roo.tree.TreePanel
30560  * @extends Roo.data.Tree
30561
30562  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30563  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30564  * @cfg {Boolean} enableDD true to enable drag and drop
30565  * @cfg {Boolean} enableDrag true to enable just drag
30566  * @cfg {Boolean} enableDrop true to enable just drop
30567  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30568  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30569  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30570  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30571  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30572  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30573  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30574  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30575  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30576  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30577  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30578  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30579  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30580  * @cfg {Function} renderer 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>
30581  * @cfg {Function} rendererTip 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>
30582  * 
30583  * @constructor
30584  * @param {String/HTMLElement/Element} el The container element
30585  * @param {Object} config
30586  */
30587 Roo.tree.TreePanel = function(el, config){
30588     var root = false;
30589     var loader = false;
30590     if (config.root) {
30591         root = config.root;
30592         delete config.root;
30593     }
30594     if (config.loader) {
30595         loader = config.loader;
30596         delete config.loader;
30597     }
30598     
30599     Roo.apply(this, config);
30600     Roo.tree.TreePanel.superclass.constructor.call(this);
30601     this.el = Roo.get(el);
30602     this.el.addClass('x-tree');
30603     //console.log(root);
30604     if (root) {
30605         this.setRootNode( Roo.factory(root, Roo.tree));
30606     }
30607     if (loader) {
30608         this.loader = Roo.factory(loader, Roo.tree);
30609     }
30610    /**
30611     * Read-only. The id of the container element becomes this TreePanel's id.
30612     */
30613    this.id = this.el.id;
30614    this.addEvents({
30615         /**
30616         * @event beforeload
30617         * Fires before a node is loaded, return false to cancel
30618         * @param {Node} node The node being loaded
30619         */
30620         "beforeload" : true,
30621         /**
30622         * @event load
30623         * Fires when a node is loaded
30624         * @param {Node} node The node that was loaded
30625         */
30626         "load" : true,
30627         /**
30628         * @event textchange
30629         * Fires when the text for a node is changed
30630         * @param {Node} node The node
30631         * @param {String} text The new text
30632         * @param {String} oldText The old text
30633         */
30634         "textchange" : true,
30635         /**
30636         * @event beforeexpand
30637         * Fires before a node is expanded, return false to cancel.
30638         * @param {Node} node The node
30639         * @param {Boolean} deep
30640         * @param {Boolean} anim
30641         */
30642         "beforeexpand" : true,
30643         /**
30644         * @event beforecollapse
30645         * Fires before a node is collapsed, return false to cancel.
30646         * @param {Node} node The node
30647         * @param {Boolean} deep
30648         * @param {Boolean} anim
30649         */
30650         "beforecollapse" : true,
30651         /**
30652         * @event expand
30653         * Fires when a node is expanded
30654         * @param {Node} node The node
30655         */
30656         "expand" : true,
30657         /**
30658         * @event disabledchange
30659         * Fires when the disabled status of a node changes
30660         * @param {Node} node The node
30661         * @param {Boolean} disabled
30662         */
30663         "disabledchange" : true,
30664         /**
30665         * @event collapse
30666         * Fires when a node is collapsed
30667         * @param {Node} node The node
30668         */
30669         "collapse" : true,
30670         /**
30671         * @event beforeclick
30672         * Fires before click processing on a node. Return false to cancel the default action.
30673         * @param {Node} node The node
30674         * @param {Roo.EventObject} e The event object
30675         */
30676         "beforeclick":true,
30677         /**
30678         * @event checkchange
30679         * Fires when a node with a checkbox's checked property changes
30680         * @param {Node} this This node
30681         * @param {Boolean} checked
30682         */
30683         "checkchange":true,
30684         /**
30685         * @event click
30686         * Fires when a node is clicked
30687         * @param {Node} node The node
30688         * @param {Roo.EventObject} e The event object
30689         */
30690         "click":true,
30691         /**
30692         * @event dblclick
30693         * Fires when a node is double clicked
30694         * @param {Node} node The node
30695         * @param {Roo.EventObject} e The event object
30696         */
30697         "dblclick":true,
30698         /**
30699         * @event contextmenu
30700         * Fires when a node is right clicked
30701         * @param {Node} node The node
30702         * @param {Roo.EventObject} e The event object
30703         */
30704         "contextmenu":true,
30705         /**
30706         * @event beforechildrenrendered
30707         * Fires right before the child nodes for a node are rendered
30708         * @param {Node} node The node
30709         */
30710         "beforechildrenrendered":true,
30711        /**
30712              * @event startdrag
30713              * Fires when a node starts being dragged
30714              * @param {Roo.tree.TreePanel} this
30715              * @param {Roo.tree.TreeNode} node
30716              * @param {event} e The raw browser event
30717              */ 
30718             "startdrag" : true,
30719             /**
30720              * @event enddrag
30721              * Fires when a drag operation is complete
30722              * @param {Roo.tree.TreePanel} this
30723              * @param {Roo.tree.TreeNode} node
30724              * @param {event} e The raw browser event
30725              */
30726             "enddrag" : true,
30727             /**
30728              * @event dragdrop
30729              * Fires when a dragged node is dropped on a valid DD target
30730              * @param {Roo.tree.TreePanel} this
30731              * @param {Roo.tree.TreeNode} node
30732              * @param {DD} dd The dd it was dropped on
30733              * @param {event} e The raw browser event
30734              */
30735             "dragdrop" : true,
30736             /**
30737              * @event beforenodedrop
30738              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30739              * passed to handlers has the following properties:<br />
30740              * <ul style="padding:5px;padding-left:16px;">
30741              * <li>tree - The TreePanel</li>
30742              * <li>target - The node being targeted for the drop</li>
30743              * <li>data - The drag data from the drag source</li>
30744              * <li>point - The point of the drop - append, above or below</li>
30745              * <li>source - The drag source</li>
30746              * <li>rawEvent - Raw mouse event</li>
30747              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30748              * to be inserted by setting them on this object.</li>
30749              * <li>cancel - Set this to true to cancel the drop.</li>
30750              * </ul>
30751              * @param {Object} dropEvent
30752              */
30753             "beforenodedrop" : true,
30754             /**
30755              * @event nodedrop
30756              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30757              * passed to handlers has the following properties:<br />
30758              * <ul style="padding:5px;padding-left:16px;">
30759              * <li>tree - The TreePanel</li>
30760              * <li>target - The node being targeted for the drop</li>
30761              * <li>data - The drag data from the drag source</li>
30762              * <li>point - The point of the drop - append, above or below</li>
30763              * <li>source - The drag source</li>
30764              * <li>rawEvent - Raw mouse event</li>
30765              * <li>dropNode - Dropped node(s).</li>
30766              * </ul>
30767              * @param {Object} dropEvent
30768              */
30769             "nodedrop" : true,
30770              /**
30771              * @event nodedragover
30772              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30773              * passed to handlers has the following properties:<br />
30774              * <ul style="padding:5px;padding-left:16px;">
30775              * <li>tree - The TreePanel</li>
30776              * <li>target - The node being targeted for the drop</li>
30777              * <li>data - The drag data from the drag source</li>
30778              * <li>point - The point of the drop - append, above or below</li>
30779              * <li>source - The drag source</li>
30780              * <li>rawEvent - Raw mouse event</li>
30781              * <li>dropNode - Drop node(s) provided by the source.</li>
30782              * <li>cancel - Set this to true to signal drop not allowed.</li>
30783              * </ul>
30784              * @param {Object} dragOverEvent
30785              */
30786             "nodedragover" : true
30787         
30788    });
30789    if(this.singleExpand){
30790        this.on("beforeexpand", this.restrictExpand, this);
30791    }
30792 };
30793 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30794     rootVisible : true,
30795     animate: Roo.enableFx,
30796     lines : true,
30797     enableDD : false,
30798     hlDrop : Roo.enableFx,
30799   
30800     renderer: false,
30801     
30802     rendererTip: false,
30803     // private
30804     restrictExpand : function(node){
30805         var p = node.parentNode;
30806         if(p){
30807             if(p.expandedChild && p.expandedChild.parentNode == p){
30808                 p.expandedChild.collapse();
30809             }
30810             p.expandedChild = node;
30811         }
30812     },
30813
30814     // private override
30815     setRootNode : function(node){
30816         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30817         if(!this.rootVisible){
30818             node.ui = new Roo.tree.RootTreeNodeUI(node);
30819         }
30820         return node;
30821     },
30822
30823     /**
30824      * Returns the container element for this TreePanel
30825      */
30826     getEl : function(){
30827         return this.el;
30828     },
30829
30830     /**
30831      * Returns the default TreeLoader for this TreePanel
30832      */
30833     getLoader : function(){
30834         return this.loader;
30835     },
30836
30837     /**
30838      * Expand all nodes
30839      */
30840     expandAll : function(){
30841         this.root.expand(true);
30842     },
30843
30844     /**
30845      * Collapse all nodes
30846      */
30847     collapseAll : function(){
30848         this.root.collapse(true);
30849     },
30850
30851     /**
30852      * Returns the selection model used by this TreePanel
30853      */
30854     getSelectionModel : function(){
30855         if(!this.selModel){
30856             this.selModel = new Roo.tree.DefaultSelectionModel();
30857         }
30858         return this.selModel;
30859     },
30860
30861     /**
30862      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30863      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30864      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30865      * @return {Array}
30866      */
30867     getChecked : function(a, startNode){
30868         startNode = startNode || this.root;
30869         var r = [];
30870         var f = function(){
30871             if(this.attributes.checked){
30872                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30873             }
30874         }
30875         startNode.cascade(f);
30876         return r;
30877     },
30878
30879     /**
30880      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30881      * @param {String} path
30882      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30883      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30884      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30885      */
30886     expandPath : function(path, attr, callback){
30887         attr = attr || "id";
30888         var keys = path.split(this.pathSeparator);
30889         var curNode = this.root;
30890         if(curNode.attributes[attr] != keys[1]){ // invalid root
30891             if(callback){
30892                 callback(false, null);
30893             }
30894             return;
30895         }
30896         var index = 1;
30897         var f = function(){
30898             if(++index == keys.length){
30899                 if(callback){
30900                     callback(true, curNode);
30901                 }
30902                 return;
30903             }
30904             var c = curNode.findChild(attr, keys[index]);
30905             if(!c){
30906                 if(callback){
30907                     callback(false, curNode);
30908                 }
30909                 return;
30910             }
30911             curNode = c;
30912             c.expand(false, false, f);
30913         };
30914         curNode.expand(false, false, f);
30915     },
30916
30917     /**
30918      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30919      * @param {String} path
30920      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30921      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30922      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30923      */
30924     selectPath : function(path, attr, callback){
30925         attr = attr || "id";
30926         var keys = path.split(this.pathSeparator);
30927         var v = keys.pop();
30928         if(keys.length > 0){
30929             var f = function(success, node){
30930                 if(success && node){
30931                     var n = node.findChild(attr, v);
30932                     if(n){
30933                         n.select();
30934                         if(callback){
30935                             callback(true, n);
30936                         }
30937                     }else if(callback){
30938                         callback(false, n);
30939                     }
30940                 }else{
30941                     if(callback){
30942                         callback(false, n);
30943                     }
30944                 }
30945             };
30946             this.expandPath(keys.join(this.pathSeparator), attr, f);
30947         }else{
30948             this.root.select();
30949             if(callback){
30950                 callback(true, this.root);
30951             }
30952         }
30953     },
30954
30955     getTreeEl : function(){
30956         return this.el;
30957     },
30958
30959     /**
30960      * Trigger rendering of this TreePanel
30961      */
30962     render : function(){
30963         if (this.innerCt) {
30964             return this; // stop it rendering more than once!!
30965         }
30966         
30967         this.innerCt = this.el.createChild({tag:"ul",
30968                cls:"x-tree-root-ct " +
30969                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30970
30971         if(this.containerScroll){
30972             Roo.dd.ScrollManager.register(this.el);
30973         }
30974         if((this.enableDD || this.enableDrop) && !this.dropZone){
30975            /**
30976             * The dropZone used by this tree if drop is enabled
30977             * @type Roo.tree.TreeDropZone
30978             */
30979              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30980                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30981            });
30982         }
30983         if((this.enableDD || this.enableDrag) && !this.dragZone){
30984            /**
30985             * The dragZone used by this tree if drag is enabled
30986             * @type Roo.tree.TreeDragZone
30987             */
30988             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30989                ddGroup: this.ddGroup || "TreeDD",
30990                scroll: this.ddScroll
30991            });
30992         }
30993         this.getSelectionModel().init(this);
30994         if (!this.root) {
30995             console.log("ROOT not set in tree");
30996             return;
30997         }
30998         this.root.render();
30999         if(!this.rootVisible){
31000             this.root.renderChildren();
31001         }
31002         return this;
31003     }
31004 });/*
31005  * Based on:
31006  * Ext JS Library 1.1.1
31007  * Copyright(c) 2006-2007, Ext JS, LLC.
31008  *
31009  * Originally Released Under LGPL - original licence link has changed is not relivant.
31010  *
31011  * Fork - LGPL
31012  * <script type="text/javascript">
31013  */
31014  
31015
31016 /**
31017  * @class Roo.tree.DefaultSelectionModel
31018  * @extends Roo.util.Observable
31019  * The default single selection for a TreePanel.
31020  */
31021 Roo.tree.DefaultSelectionModel = function(){
31022    this.selNode = null;
31023    
31024    this.addEvents({
31025        /**
31026         * @event selectionchange
31027         * Fires when the selected node changes
31028         * @param {DefaultSelectionModel} this
31029         * @param {TreeNode} node the new selection
31030         */
31031        "selectionchange" : true,
31032
31033        /**
31034         * @event beforeselect
31035         * Fires before the selected node changes, return false to cancel the change
31036         * @param {DefaultSelectionModel} this
31037         * @param {TreeNode} node the new selection
31038         * @param {TreeNode} node the old selection
31039         */
31040        "beforeselect" : true
31041    });
31042 };
31043
31044 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31045     init : function(tree){
31046         this.tree = tree;
31047         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31048         tree.on("click", this.onNodeClick, this);
31049     },
31050     
31051     onNodeClick : function(node, e){
31052         if (e.ctrlKey && this.selNode == node)  {
31053             this.unselect(node);
31054             return;
31055         }
31056         this.select(node);
31057     },
31058     
31059     /**
31060      * Select a node.
31061      * @param {TreeNode} node The node to select
31062      * @return {TreeNode} The selected node
31063      */
31064     select : function(node){
31065         var last = this.selNode;
31066         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31067             if(last){
31068                 last.ui.onSelectedChange(false);
31069             }
31070             this.selNode = node;
31071             node.ui.onSelectedChange(true);
31072             this.fireEvent("selectionchange", this, node, last);
31073         }
31074         return node;
31075     },
31076     
31077     /**
31078      * Deselect a node.
31079      * @param {TreeNode} node The node to unselect
31080      */
31081     unselect : function(node){
31082         if(this.selNode == node){
31083             this.clearSelections();
31084         }    
31085     },
31086     
31087     /**
31088      * Clear all selections
31089      */
31090     clearSelections : function(){
31091         var n = this.selNode;
31092         if(n){
31093             n.ui.onSelectedChange(false);
31094             this.selNode = null;
31095             this.fireEvent("selectionchange", this, null);
31096         }
31097         return n;
31098     },
31099     
31100     /**
31101      * Get the selected node
31102      * @return {TreeNode} The selected node
31103      */
31104     getSelectedNode : function(){
31105         return this.selNode;    
31106     },
31107     
31108     /**
31109      * Returns true if the node is selected
31110      * @param {TreeNode} node The node to check
31111      * @return {Boolean}
31112      */
31113     isSelected : function(node){
31114         return this.selNode == node;  
31115     },
31116
31117     /**
31118      * Selects the node above the selected node in the tree, intelligently walking the nodes
31119      * @return TreeNode The new selection
31120      */
31121     selectPrevious : function(){
31122         var s = this.selNode || this.lastSelNode;
31123         if(!s){
31124             return null;
31125         }
31126         var ps = s.previousSibling;
31127         if(ps){
31128             if(!ps.isExpanded() || ps.childNodes.length < 1){
31129                 return this.select(ps);
31130             } else{
31131                 var lc = ps.lastChild;
31132                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31133                     lc = lc.lastChild;
31134                 }
31135                 return this.select(lc);
31136             }
31137         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31138             return this.select(s.parentNode);
31139         }
31140         return null;
31141     },
31142
31143     /**
31144      * Selects the node above the selected node in the tree, intelligently walking the nodes
31145      * @return TreeNode The new selection
31146      */
31147     selectNext : function(){
31148         var s = this.selNode || this.lastSelNode;
31149         if(!s){
31150             return null;
31151         }
31152         if(s.firstChild && s.isExpanded()){
31153              return this.select(s.firstChild);
31154          }else if(s.nextSibling){
31155              return this.select(s.nextSibling);
31156          }else if(s.parentNode){
31157             var newS = null;
31158             s.parentNode.bubble(function(){
31159                 if(this.nextSibling){
31160                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31161                     return false;
31162                 }
31163             });
31164             return newS;
31165          }
31166         return null;
31167     },
31168
31169     onKeyDown : function(e){
31170         var s = this.selNode || this.lastSelNode;
31171         // undesirable, but required
31172         var sm = this;
31173         if(!s){
31174             return;
31175         }
31176         var k = e.getKey();
31177         switch(k){
31178              case e.DOWN:
31179                  e.stopEvent();
31180                  this.selectNext();
31181              break;
31182              case e.UP:
31183                  e.stopEvent();
31184                  this.selectPrevious();
31185              break;
31186              case e.RIGHT:
31187                  e.preventDefault();
31188                  if(s.hasChildNodes()){
31189                      if(!s.isExpanded()){
31190                          s.expand();
31191                      }else if(s.firstChild){
31192                          this.select(s.firstChild, e);
31193                      }
31194                  }
31195              break;
31196              case e.LEFT:
31197                  e.preventDefault();
31198                  if(s.hasChildNodes() && s.isExpanded()){
31199                      s.collapse();
31200                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31201                      this.select(s.parentNode, e);
31202                  }
31203              break;
31204         };
31205     }
31206 });
31207
31208 /**
31209  * @class Roo.tree.MultiSelectionModel
31210  * @extends Roo.util.Observable
31211  * Multi selection for a TreePanel.
31212  */
31213 Roo.tree.MultiSelectionModel = function(){
31214    this.selNodes = [];
31215    this.selMap = {};
31216    this.addEvents({
31217        /**
31218         * @event selectionchange
31219         * Fires when the selected nodes change
31220         * @param {MultiSelectionModel} this
31221         * @param {Array} nodes Array of the selected nodes
31222         */
31223        "selectionchange" : true
31224    });
31225 };
31226
31227 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31228     init : function(tree){
31229         this.tree = tree;
31230         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31231         tree.on("click", this.onNodeClick, this);
31232     },
31233     
31234     onNodeClick : function(node, e){
31235         this.select(node, e, e.ctrlKey);
31236     },
31237     
31238     /**
31239      * Select a node.
31240      * @param {TreeNode} node The node to select
31241      * @param {EventObject} e (optional) An event associated with the selection
31242      * @param {Boolean} keepExisting True to retain existing selections
31243      * @return {TreeNode} The selected node
31244      */
31245     select : function(node, e, keepExisting){
31246         if(keepExisting !== true){
31247             this.clearSelections(true);
31248         }
31249         if(this.isSelected(node)){
31250             this.lastSelNode = node;
31251             return node;
31252         }
31253         this.selNodes.push(node);
31254         this.selMap[node.id] = node;
31255         this.lastSelNode = node;
31256         node.ui.onSelectedChange(true);
31257         this.fireEvent("selectionchange", this, this.selNodes);
31258         return node;
31259     },
31260     
31261     /**
31262      * Deselect a node.
31263      * @param {TreeNode} node The node to unselect
31264      */
31265     unselect : function(node){
31266         if(this.selMap[node.id]){
31267             node.ui.onSelectedChange(false);
31268             var sn = this.selNodes;
31269             var index = -1;
31270             if(sn.indexOf){
31271                 index = sn.indexOf(node);
31272             }else{
31273                 for(var i = 0, len = sn.length; i < len; i++){
31274                     if(sn[i] == node){
31275                         index = i;
31276                         break;
31277                     }
31278                 }
31279             }
31280             if(index != -1){
31281                 this.selNodes.splice(index, 1);
31282             }
31283             delete this.selMap[node.id];
31284             this.fireEvent("selectionchange", this, this.selNodes);
31285         }
31286     },
31287     
31288     /**
31289      * Clear all selections
31290      */
31291     clearSelections : function(suppressEvent){
31292         var sn = this.selNodes;
31293         if(sn.length > 0){
31294             for(var i = 0, len = sn.length; i < len; i++){
31295                 sn[i].ui.onSelectedChange(false);
31296             }
31297             this.selNodes = [];
31298             this.selMap = {};
31299             if(suppressEvent !== true){
31300                 this.fireEvent("selectionchange", this, this.selNodes);
31301             }
31302         }
31303     },
31304     
31305     /**
31306      * Returns true if the node is selected
31307      * @param {TreeNode} node The node to check
31308      * @return {Boolean}
31309      */
31310     isSelected : function(node){
31311         return this.selMap[node.id] ? true : false;  
31312     },
31313     
31314     /**
31315      * Returns an array of the selected nodes
31316      * @return {Array}
31317      */
31318     getSelectedNodes : function(){
31319         return this.selNodes;    
31320     },
31321
31322     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31323
31324     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31325
31326     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31327 });/*
31328  * Based on:
31329  * Ext JS Library 1.1.1
31330  * Copyright(c) 2006-2007, Ext JS, LLC.
31331  *
31332  * Originally Released Under LGPL - original licence link has changed is not relivant.
31333  *
31334  * Fork - LGPL
31335  * <script type="text/javascript">
31336  */
31337  
31338 /**
31339  * @class Roo.tree.TreeNode
31340  * @extends Roo.data.Node
31341  * @cfg {String} text The text for this node
31342  * @cfg {Boolean} expanded true to start the node expanded
31343  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31344  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31345  * @cfg {Boolean} disabled true to start the node disabled
31346  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31347  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31348  * @cfg {String} cls A css class to be added to the node
31349  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31350  * @cfg {String} href URL of the link used for the node (defaults to #)
31351  * @cfg {String} hrefTarget target frame for the link
31352  * @cfg {String} qtip An Ext QuickTip for the node
31353  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31354  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31355  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31356  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31357  * (defaults to undefined with no checkbox rendered)
31358  * @constructor
31359  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31360  */
31361 Roo.tree.TreeNode = function(attributes){
31362     attributes = attributes || {};
31363     if(typeof attributes == "string"){
31364         attributes = {text: attributes};
31365     }
31366     this.childrenRendered = false;
31367     this.rendered = false;
31368     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31369     this.expanded = attributes.expanded === true;
31370     this.isTarget = attributes.isTarget !== false;
31371     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31372     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31373
31374     /**
31375      * Read-only. The text for this node. To change it use setText().
31376      * @type String
31377      */
31378     this.text = attributes.text;
31379     /**
31380      * True if this node is disabled.
31381      * @type Boolean
31382      */
31383     this.disabled = attributes.disabled === true;
31384
31385     this.addEvents({
31386         /**
31387         * @event textchange
31388         * Fires when the text for this node is changed
31389         * @param {Node} this This node
31390         * @param {String} text The new text
31391         * @param {String} oldText The old text
31392         */
31393         "textchange" : true,
31394         /**
31395         * @event beforeexpand
31396         * Fires before this node is expanded, return false to cancel.
31397         * @param {Node} this This node
31398         * @param {Boolean} deep
31399         * @param {Boolean} anim
31400         */
31401         "beforeexpand" : true,
31402         /**
31403         * @event beforecollapse
31404         * Fires before this node is collapsed, return false to cancel.
31405         * @param {Node} this This node
31406         * @param {Boolean} deep
31407         * @param {Boolean} anim
31408         */
31409         "beforecollapse" : true,
31410         /**
31411         * @event expand
31412         * Fires when this node is expanded
31413         * @param {Node} this This node
31414         */
31415         "expand" : true,
31416         /**
31417         * @event disabledchange
31418         * Fires when the disabled status of this node changes
31419         * @param {Node} this This node
31420         * @param {Boolean} disabled
31421         */
31422         "disabledchange" : true,
31423         /**
31424         * @event collapse
31425         * Fires when this node is collapsed
31426         * @param {Node} this This node
31427         */
31428         "collapse" : true,
31429         /**
31430         * @event beforeclick
31431         * Fires before click processing. Return false to cancel the default action.
31432         * @param {Node} this This node
31433         * @param {Roo.EventObject} e The event object
31434         */
31435         "beforeclick":true,
31436         /**
31437         * @event checkchange
31438         * Fires when a node with a checkbox's checked property changes
31439         * @param {Node} this This node
31440         * @param {Boolean} checked
31441         */
31442         "checkchange":true,
31443         /**
31444         * @event click
31445         * Fires when this node is clicked
31446         * @param {Node} this This node
31447         * @param {Roo.EventObject} e The event object
31448         */
31449         "click":true,
31450         /**
31451         * @event dblclick
31452         * Fires when this node is double clicked
31453         * @param {Node} this This node
31454         * @param {Roo.EventObject} e The event object
31455         */
31456         "dblclick":true,
31457         /**
31458         * @event contextmenu
31459         * Fires when this node is right clicked
31460         * @param {Node} this This node
31461         * @param {Roo.EventObject} e The event object
31462         */
31463         "contextmenu":true,
31464         /**
31465         * @event beforechildrenrendered
31466         * Fires right before the child nodes for this node are rendered
31467         * @param {Node} this This node
31468         */
31469         "beforechildrenrendered":true
31470     });
31471
31472     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31473
31474     /**
31475      * Read-only. The UI for this node
31476      * @type TreeNodeUI
31477      */
31478     this.ui = new uiClass(this);
31479 };
31480 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31481     preventHScroll: true,
31482     /**
31483      * Returns true if this node is expanded
31484      * @return {Boolean}
31485      */
31486     isExpanded : function(){
31487         return this.expanded;
31488     },
31489
31490     /**
31491      * Returns the UI object for this node
31492      * @return {TreeNodeUI}
31493      */
31494     getUI : function(){
31495         return this.ui;
31496     },
31497
31498     // private override
31499     setFirstChild : function(node){
31500         var of = this.firstChild;
31501         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31502         if(this.childrenRendered && of && node != of){
31503             of.renderIndent(true, true);
31504         }
31505         if(this.rendered){
31506             this.renderIndent(true, true);
31507         }
31508     },
31509
31510     // private override
31511     setLastChild : function(node){
31512         var ol = this.lastChild;
31513         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31514         if(this.childrenRendered && ol && node != ol){
31515             ol.renderIndent(true, true);
31516         }
31517         if(this.rendered){
31518             this.renderIndent(true, true);
31519         }
31520     },
31521
31522     // these methods are overridden to provide lazy rendering support
31523     // private override
31524     appendChild : function(){
31525         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31526         if(node && this.childrenRendered){
31527             node.render();
31528         }
31529         this.ui.updateExpandIcon();
31530         return node;
31531     },
31532
31533     // private override
31534     removeChild : function(node){
31535         this.ownerTree.getSelectionModel().unselect(node);
31536         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31537         // if it's been rendered remove dom node
31538         if(this.childrenRendered){
31539             node.ui.remove();
31540         }
31541         if(this.childNodes.length < 1){
31542             this.collapse(false, false);
31543         }else{
31544             this.ui.updateExpandIcon();
31545         }
31546         if(!this.firstChild) {
31547             this.childrenRendered = false;
31548         }
31549         return node;
31550     },
31551
31552     // private override
31553     insertBefore : function(node, refNode){
31554         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31555         if(newNode && refNode && this.childrenRendered){
31556             node.render();
31557         }
31558         this.ui.updateExpandIcon();
31559         return newNode;
31560     },
31561
31562     /**
31563      * Sets the text for this node
31564      * @param {String} text
31565      */
31566     setText : function(text){
31567         var oldText = this.text;
31568         this.text = text;
31569         this.attributes.text = text;
31570         if(this.rendered){ // event without subscribing
31571             this.ui.onTextChange(this, text, oldText);
31572         }
31573         this.fireEvent("textchange", this, text, oldText);
31574     },
31575
31576     /**
31577      * Triggers selection of this node
31578      */
31579     select : function(){
31580         this.getOwnerTree().getSelectionModel().select(this);
31581     },
31582
31583     /**
31584      * Triggers deselection of this node
31585      */
31586     unselect : function(){
31587         this.getOwnerTree().getSelectionModel().unselect(this);
31588     },
31589
31590     /**
31591      * Returns true if this node is selected
31592      * @return {Boolean}
31593      */
31594     isSelected : function(){
31595         return this.getOwnerTree().getSelectionModel().isSelected(this);
31596     },
31597
31598     /**
31599      * Expand this node.
31600      * @param {Boolean} deep (optional) True to expand all children as well
31601      * @param {Boolean} anim (optional) false to cancel the default animation
31602      * @param {Function} callback (optional) A callback to be called when
31603      * expanding this node completes (does not wait for deep expand to complete).
31604      * Called with 1 parameter, this node.
31605      */
31606     expand : function(deep, anim, callback){
31607         if(!this.expanded){
31608             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31609                 return;
31610             }
31611             if(!this.childrenRendered){
31612                 this.renderChildren();
31613             }
31614             this.expanded = true;
31615             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31616                 this.ui.animExpand(function(){
31617                     this.fireEvent("expand", this);
31618                     if(typeof callback == "function"){
31619                         callback(this);
31620                     }
31621                     if(deep === true){
31622                         this.expandChildNodes(true);
31623                     }
31624                 }.createDelegate(this));
31625                 return;
31626             }else{
31627                 this.ui.expand();
31628                 this.fireEvent("expand", this);
31629                 if(typeof callback == "function"){
31630                     callback(this);
31631                 }
31632             }
31633         }else{
31634            if(typeof callback == "function"){
31635                callback(this);
31636            }
31637         }
31638         if(deep === true){
31639             this.expandChildNodes(true);
31640         }
31641     },
31642
31643     isHiddenRoot : function(){
31644         return this.isRoot && !this.getOwnerTree().rootVisible;
31645     },
31646
31647     /**
31648      * Collapse this node.
31649      * @param {Boolean} deep (optional) True to collapse all children as well
31650      * @param {Boolean} anim (optional) false to cancel the default animation
31651      */
31652     collapse : function(deep, anim){
31653         if(this.expanded && !this.isHiddenRoot()){
31654             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31655                 return;
31656             }
31657             this.expanded = false;
31658             if((this.getOwnerTree().animate && anim !== false) || anim){
31659                 this.ui.animCollapse(function(){
31660                     this.fireEvent("collapse", this);
31661                     if(deep === true){
31662                         this.collapseChildNodes(true);
31663                     }
31664                 }.createDelegate(this));
31665                 return;
31666             }else{
31667                 this.ui.collapse();
31668                 this.fireEvent("collapse", this);
31669             }
31670         }
31671         if(deep === true){
31672             var cs = this.childNodes;
31673             for(var i = 0, len = cs.length; i < len; i++) {
31674                 cs[i].collapse(true, false);
31675             }
31676         }
31677     },
31678
31679     // private
31680     delayedExpand : function(delay){
31681         if(!this.expandProcId){
31682             this.expandProcId = this.expand.defer(delay, this);
31683         }
31684     },
31685
31686     // private
31687     cancelExpand : function(){
31688         if(this.expandProcId){
31689             clearTimeout(this.expandProcId);
31690         }
31691         this.expandProcId = false;
31692     },
31693
31694     /**
31695      * Toggles expanded/collapsed state of the node
31696      */
31697     toggle : function(){
31698         if(this.expanded){
31699             this.collapse();
31700         }else{
31701             this.expand();
31702         }
31703     },
31704
31705     /**
31706      * Ensures all parent nodes are expanded
31707      */
31708     ensureVisible : function(callback){
31709         var tree = this.getOwnerTree();
31710         tree.expandPath(this.parentNode.getPath(), false, function(){
31711             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31712             Roo.callback(callback);
31713         }.createDelegate(this));
31714     },
31715
31716     /**
31717      * Expand all child nodes
31718      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31719      */
31720     expandChildNodes : function(deep){
31721         var cs = this.childNodes;
31722         for(var i = 0, len = cs.length; i < len; i++) {
31723                 cs[i].expand(deep);
31724         }
31725     },
31726
31727     /**
31728      * Collapse all child nodes
31729      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31730      */
31731     collapseChildNodes : function(deep){
31732         var cs = this.childNodes;
31733         for(var i = 0, len = cs.length; i < len; i++) {
31734                 cs[i].collapse(deep);
31735         }
31736     },
31737
31738     /**
31739      * Disables this node
31740      */
31741     disable : function(){
31742         this.disabled = true;
31743         this.unselect();
31744         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31745             this.ui.onDisableChange(this, true);
31746         }
31747         this.fireEvent("disabledchange", this, true);
31748     },
31749
31750     /**
31751      * Enables this node
31752      */
31753     enable : function(){
31754         this.disabled = false;
31755         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31756             this.ui.onDisableChange(this, false);
31757         }
31758         this.fireEvent("disabledchange", this, false);
31759     },
31760
31761     // private
31762     renderChildren : function(suppressEvent){
31763         if(suppressEvent !== false){
31764             this.fireEvent("beforechildrenrendered", this);
31765         }
31766         var cs = this.childNodes;
31767         for(var i = 0, len = cs.length; i < len; i++){
31768             cs[i].render(true);
31769         }
31770         this.childrenRendered = true;
31771     },
31772
31773     // private
31774     sort : function(fn, scope){
31775         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31776         if(this.childrenRendered){
31777             var cs = this.childNodes;
31778             for(var i = 0, len = cs.length; i < len; i++){
31779                 cs[i].render(true);
31780             }
31781         }
31782     },
31783
31784     // private
31785     render : function(bulkRender){
31786         this.ui.render(bulkRender);
31787         if(!this.rendered){
31788             this.rendered = true;
31789             if(this.expanded){
31790                 this.expanded = false;
31791                 this.expand(false, false);
31792             }
31793         }
31794     },
31795
31796     // private
31797     renderIndent : function(deep, refresh){
31798         if(refresh){
31799             this.ui.childIndent = null;
31800         }
31801         this.ui.renderIndent();
31802         if(deep === true && this.childrenRendered){
31803             var cs = this.childNodes;
31804             for(var i = 0, len = cs.length; i < len; i++){
31805                 cs[i].renderIndent(true, refresh);
31806             }
31807         }
31808     }
31809 });/*
31810  * Based on:
31811  * Ext JS Library 1.1.1
31812  * Copyright(c) 2006-2007, Ext JS, LLC.
31813  *
31814  * Originally Released Under LGPL - original licence link has changed is not relivant.
31815  *
31816  * Fork - LGPL
31817  * <script type="text/javascript">
31818  */
31819  
31820 /**
31821  * @class Roo.tree.AsyncTreeNode
31822  * @extends Roo.tree.TreeNode
31823  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31824  * @constructor
31825  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31826  */
31827  Roo.tree.AsyncTreeNode = function(config){
31828     this.loaded = false;
31829     this.loading = false;
31830     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31831     /**
31832     * @event beforeload
31833     * Fires before this node is loaded, return false to cancel
31834     * @param {Node} this This node
31835     */
31836     this.addEvents({'beforeload':true, 'load': true});
31837     /**
31838     * @event load
31839     * Fires when this node is loaded
31840     * @param {Node} this This node
31841     */
31842     /**
31843      * The loader used by this node (defaults to using the tree's defined loader)
31844      * @type TreeLoader
31845      * @property loader
31846      */
31847 };
31848 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31849     expand : function(deep, anim, callback){
31850         if(this.loading){ // if an async load is already running, waiting til it's done
31851             var timer;
31852             var f = function(){
31853                 if(!this.loading){ // done loading
31854                     clearInterval(timer);
31855                     this.expand(deep, anim, callback);
31856                 }
31857             }.createDelegate(this);
31858             timer = setInterval(f, 200);
31859             return;
31860         }
31861         if(!this.loaded){
31862             if(this.fireEvent("beforeload", this) === false){
31863                 return;
31864             }
31865             this.loading = true;
31866             this.ui.beforeLoad(this);
31867             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31868             if(loader){
31869                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31870                 return;
31871             }
31872         }
31873         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31874     },
31875     
31876     /**
31877      * Returns true if this node is currently loading
31878      * @return {Boolean}
31879      */
31880     isLoading : function(){
31881         return this.loading;  
31882     },
31883     
31884     loadComplete : function(deep, anim, callback){
31885         this.loading = false;
31886         this.loaded = true;
31887         this.ui.afterLoad(this);
31888         this.fireEvent("load", this);
31889         this.expand(deep, anim, callback);
31890     },
31891     
31892     /**
31893      * Returns true if this node has been loaded
31894      * @return {Boolean}
31895      */
31896     isLoaded : function(){
31897         return this.loaded;
31898     },
31899     
31900     hasChildNodes : function(){
31901         if(!this.isLeaf() && !this.loaded){
31902             return true;
31903         }else{
31904             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31905         }
31906     },
31907
31908     /**
31909      * Trigger a reload for this node
31910      * @param {Function} callback
31911      */
31912     reload : function(callback){
31913         this.collapse(false, false);
31914         while(this.firstChild){
31915             this.removeChild(this.firstChild);
31916         }
31917         this.childrenRendered = false;
31918         this.loaded = false;
31919         if(this.isHiddenRoot()){
31920             this.expanded = false;
31921         }
31922         this.expand(false, false, callback);
31923     }
31924 });/*
31925  * Based on:
31926  * Ext JS Library 1.1.1
31927  * Copyright(c) 2006-2007, Ext JS, LLC.
31928  *
31929  * Originally Released Under LGPL - original licence link has changed is not relivant.
31930  *
31931  * Fork - LGPL
31932  * <script type="text/javascript">
31933  */
31934  
31935 /**
31936  * @class Roo.tree.TreeNodeUI
31937  * @constructor
31938  * @param {Object} node The node to render
31939  * The TreeNode UI implementation is separate from the
31940  * tree implementation. Unless you are customizing the tree UI,
31941  * you should never have to use this directly.
31942  */
31943 Roo.tree.TreeNodeUI = function(node){
31944     this.node = node;
31945     this.rendered = false;
31946     this.animating = false;
31947     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31948 };
31949
31950 Roo.tree.TreeNodeUI.prototype = {
31951     removeChild : function(node){
31952         if(this.rendered){
31953             this.ctNode.removeChild(node.ui.getEl());
31954         }
31955     },
31956
31957     beforeLoad : function(){
31958          this.addClass("x-tree-node-loading");
31959     },
31960
31961     afterLoad : function(){
31962          this.removeClass("x-tree-node-loading");
31963     },
31964
31965     onTextChange : function(node, text, oldText){
31966         if(this.rendered){
31967             this.textNode.innerHTML = text;
31968         }
31969     },
31970
31971     onDisableChange : function(node, state){
31972         this.disabled = state;
31973         if(state){
31974             this.addClass("x-tree-node-disabled");
31975         }else{
31976             this.removeClass("x-tree-node-disabled");
31977         }
31978     },
31979
31980     onSelectedChange : function(state){
31981         if(state){
31982             this.focus();
31983             this.addClass("x-tree-selected");
31984         }else{
31985             //this.blur();
31986             this.removeClass("x-tree-selected");
31987         }
31988     },
31989
31990     onMove : function(tree, node, oldParent, newParent, index, refNode){
31991         this.childIndent = null;
31992         if(this.rendered){
31993             var targetNode = newParent.ui.getContainer();
31994             if(!targetNode){//target not rendered
31995                 this.holder = document.createElement("div");
31996                 this.holder.appendChild(this.wrap);
31997                 return;
31998             }
31999             var insertBefore = refNode ? refNode.ui.getEl() : null;
32000             if(insertBefore){
32001                 targetNode.insertBefore(this.wrap, insertBefore);
32002             }else{
32003                 targetNode.appendChild(this.wrap);
32004             }
32005             this.node.renderIndent(true);
32006         }
32007     },
32008
32009     addClass : function(cls){
32010         if(this.elNode){
32011             Roo.fly(this.elNode).addClass(cls);
32012         }
32013     },
32014
32015     removeClass : function(cls){
32016         if(this.elNode){
32017             Roo.fly(this.elNode).removeClass(cls);
32018         }
32019     },
32020
32021     remove : function(){
32022         if(this.rendered){
32023             this.holder = document.createElement("div");
32024             this.holder.appendChild(this.wrap);
32025         }
32026     },
32027
32028     fireEvent : function(){
32029         return this.node.fireEvent.apply(this.node, arguments);
32030     },
32031
32032     initEvents : function(){
32033         this.node.on("move", this.onMove, this);
32034         var E = Roo.EventManager;
32035         var a = this.anchor;
32036
32037         var el = Roo.fly(a, '_treeui');
32038
32039         if(Roo.isOpera){ // opera render bug ignores the CSS
32040             el.setStyle("text-decoration", "none");
32041         }
32042
32043         el.on("click", this.onClick, this);
32044         el.on("dblclick", this.onDblClick, this);
32045
32046         if(this.checkbox){
32047             Roo.EventManager.on(this.checkbox,
32048                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32049         }
32050
32051         el.on("contextmenu", this.onContextMenu, this);
32052
32053         var icon = Roo.fly(this.iconNode);
32054         icon.on("click", this.onClick, this);
32055         icon.on("dblclick", this.onDblClick, this);
32056         icon.on("contextmenu", this.onContextMenu, this);
32057         E.on(this.ecNode, "click", this.ecClick, this, true);
32058
32059         if(this.node.disabled){
32060             this.addClass("x-tree-node-disabled");
32061         }
32062         if(this.node.hidden){
32063             this.addClass("x-tree-node-disabled");
32064         }
32065         var ot = this.node.getOwnerTree();
32066         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32067         if(dd && (!this.node.isRoot || ot.rootVisible)){
32068             Roo.dd.Registry.register(this.elNode, {
32069                 node: this.node,
32070                 handles: this.getDDHandles(),
32071                 isHandle: false
32072             });
32073         }
32074     },
32075
32076     getDDHandles : function(){
32077         return [this.iconNode, this.textNode];
32078     },
32079
32080     hide : function(){
32081         if(this.rendered){
32082             this.wrap.style.display = "none";
32083         }
32084     },
32085
32086     show : function(){
32087         if(this.rendered){
32088             this.wrap.style.display = "";
32089         }
32090     },
32091
32092     onContextMenu : function(e){
32093         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32094             e.preventDefault();
32095             this.focus();
32096             this.fireEvent("contextmenu", this.node, e);
32097         }
32098     },
32099
32100     onClick : function(e){
32101         if(this.dropping){
32102             e.stopEvent();
32103             return;
32104         }
32105         if(this.fireEvent("beforeclick", this.node, e) !== false){
32106             if(!this.disabled && this.node.attributes.href){
32107                 this.fireEvent("click", this.node, e);
32108                 return;
32109             }
32110             e.preventDefault();
32111             if(this.disabled){
32112                 return;
32113             }
32114
32115             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32116                 this.node.toggle();
32117             }
32118
32119             this.fireEvent("click", this.node, e);
32120         }else{
32121             e.stopEvent();
32122         }
32123     },
32124
32125     onDblClick : function(e){
32126         e.preventDefault();
32127         if(this.disabled){
32128             return;
32129         }
32130         if(this.checkbox){
32131             this.toggleCheck();
32132         }
32133         if(!this.animating && this.node.hasChildNodes()){
32134             this.node.toggle();
32135         }
32136         this.fireEvent("dblclick", this.node, e);
32137     },
32138
32139     onCheckChange : function(){
32140         var checked = this.checkbox.checked;
32141         this.node.attributes.checked = checked;
32142         this.fireEvent('checkchange', this.node, checked);
32143     },
32144
32145     ecClick : function(e){
32146         if(!this.animating && this.node.hasChildNodes()){
32147             this.node.toggle();
32148         }
32149     },
32150
32151     startDrop : function(){
32152         this.dropping = true;
32153     },
32154
32155     // delayed drop so the click event doesn't get fired on a drop
32156     endDrop : function(){
32157        setTimeout(function(){
32158            this.dropping = false;
32159        }.createDelegate(this), 50);
32160     },
32161
32162     expand : function(){
32163         this.updateExpandIcon();
32164         this.ctNode.style.display = "";
32165     },
32166
32167     focus : function(){
32168         if(!this.node.preventHScroll){
32169             try{this.anchor.focus();
32170             }catch(e){}
32171         }else if(!Roo.isIE){
32172             try{
32173                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32174                 var l = noscroll.scrollLeft;
32175                 this.anchor.focus();
32176                 noscroll.scrollLeft = l;
32177             }catch(e){}
32178         }
32179     },
32180
32181     toggleCheck : function(value){
32182         var cb = this.checkbox;
32183         if(cb){
32184             cb.checked = (value === undefined ? !cb.checked : value);
32185         }
32186     },
32187
32188     blur : function(){
32189         try{
32190             this.anchor.blur();
32191         }catch(e){}
32192     },
32193
32194     animExpand : function(callback){
32195         var ct = Roo.get(this.ctNode);
32196         ct.stopFx();
32197         if(!this.node.hasChildNodes()){
32198             this.updateExpandIcon();
32199             this.ctNode.style.display = "";
32200             Roo.callback(callback);
32201             return;
32202         }
32203         this.animating = true;
32204         this.updateExpandIcon();
32205
32206         ct.slideIn('t', {
32207            callback : function(){
32208                this.animating = false;
32209                Roo.callback(callback);
32210             },
32211             scope: this,
32212             duration: this.node.ownerTree.duration || .25
32213         });
32214     },
32215
32216     highlight : function(){
32217         var tree = this.node.getOwnerTree();
32218         Roo.fly(this.wrap).highlight(
32219             tree.hlColor || "C3DAF9",
32220             {endColor: tree.hlBaseColor}
32221         );
32222     },
32223
32224     collapse : function(){
32225         this.updateExpandIcon();
32226         this.ctNode.style.display = "none";
32227     },
32228
32229     animCollapse : function(callback){
32230         var ct = Roo.get(this.ctNode);
32231         ct.enableDisplayMode('block');
32232         ct.stopFx();
32233
32234         this.animating = true;
32235         this.updateExpandIcon();
32236
32237         ct.slideOut('t', {
32238             callback : function(){
32239                this.animating = false;
32240                Roo.callback(callback);
32241             },
32242             scope: this,
32243             duration: this.node.ownerTree.duration || .25
32244         });
32245     },
32246
32247     getContainer : function(){
32248         return this.ctNode;
32249     },
32250
32251     getEl : function(){
32252         return this.wrap;
32253     },
32254
32255     appendDDGhost : function(ghostNode){
32256         ghostNode.appendChild(this.elNode.cloneNode(true));
32257     },
32258
32259     getDDRepairXY : function(){
32260         return Roo.lib.Dom.getXY(this.iconNode);
32261     },
32262
32263     onRender : function(){
32264         this.render();
32265     },
32266
32267     render : function(bulkRender){
32268         var n = this.node, a = n.attributes;
32269         var targetNode = n.parentNode ?
32270               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32271
32272         if(!this.rendered){
32273             this.rendered = true;
32274
32275             this.renderElements(n, a, targetNode, bulkRender);
32276
32277             if(a.qtip){
32278                if(this.textNode.setAttributeNS){
32279                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32280                    if(a.qtipTitle){
32281                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32282                    }
32283                }else{
32284                    this.textNode.setAttribute("ext:qtip", a.qtip);
32285                    if(a.qtipTitle){
32286                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32287                    }
32288                }
32289             }else if(a.qtipCfg){
32290                 a.qtipCfg.target = Roo.id(this.textNode);
32291                 Roo.QuickTips.register(a.qtipCfg);
32292             }
32293             this.initEvents();
32294             if(!this.node.expanded){
32295                 this.updateExpandIcon();
32296             }
32297         }else{
32298             if(bulkRender === true) {
32299                 targetNode.appendChild(this.wrap);
32300             }
32301         }
32302     },
32303
32304     renderElements : function(n, a, targetNode, bulkRender){
32305         // add some indent caching, this helps performance when rendering a large tree
32306         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32307         var t = n.getOwnerTree();
32308         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32309         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32310         var cb = typeof a.checked == 'boolean';
32311         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32312         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32313             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32314             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32315             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32316             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32317             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32318              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32319                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32320             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32321             "</li>"];
32322
32323         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32324             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32325                                 n.nextSibling.ui.getEl(), buf.join(""));
32326         }else{
32327             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32328         }
32329
32330         this.elNode = this.wrap.childNodes[0];
32331         this.ctNode = this.wrap.childNodes[1];
32332         var cs = this.elNode.childNodes;
32333         this.indentNode = cs[0];
32334         this.ecNode = cs[1];
32335         this.iconNode = cs[2];
32336         var index = 3;
32337         if(cb){
32338             this.checkbox = cs[3];
32339             index++;
32340         }
32341         this.anchor = cs[index];
32342         this.textNode = cs[index].firstChild;
32343     },
32344
32345     getAnchor : function(){
32346         return this.anchor;
32347     },
32348
32349     getTextEl : function(){
32350         return this.textNode;
32351     },
32352
32353     getIconEl : function(){
32354         return this.iconNode;
32355     },
32356
32357     isChecked : function(){
32358         return this.checkbox ? this.checkbox.checked : false;
32359     },
32360
32361     updateExpandIcon : function(){
32362         if(this.rendered){
32363             var n = this.node, c1, c2;
32364             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32365             var hasChild = n.hasChildNodes();
32366             if(hasChild){
32367                 if(n.expanded){
32368                     cls += "-minus";
32369                     c1 = "x-tree-node-collapsed";
32370                     c2 = "x-tree-node-expanded";
32371                 }else{
32372                     cls += "-plus";
32373                     c1 = "x-tree-node-expanded";
32374                     c2 = "x-tree-node-collapsed";
32375                 }
32376                 if(this.wasLeaf){
32377                     this.removeClass("x-tree-node-leaf");
32378                     this.wasLeaf = false;
32379                 }
32380                 if(this.c1 != c1 || this.c2 != c2){
32381                     Roo.fly(this.elNode).replaceClass(c1, c2);
32382                     this.c1 = c1; this.c2 = c2;
32383                 }
32384             }else{
32385                 if(!this.wasLeaf){
32386                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32387                     delete this.c1;
32388                     delete this.c2;
32389                     this.wasLeaf = true;
32390                 }
32391             }
32392             var ecc = "x-tree-ec-icon "+cls;
32393             if(this.ecc != ecc){
32394                 this.ecNode.className = ecc;
32395                 this.ecc = ecc;
32396             }
32397         }
32398     },
32399
32400     getChildIndent : function(){
32401         if(!this.childIndent){
32402             var buf = [];
32403             var p = this.node;
32404             while(p){
32405                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32406                     if(!p.isLast()) {
32407                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32408                     } else {
32409                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32410                     }
32411                 }
32412                 p = p.parentNode;
32413             }
32414             this.childIndent = buf.join("");
32415         }
32416         return this.childIndent;
32417     },
32418
32419     renderIndent : function(){
32420         if(this.rendered){
32421             var indent = "";
32422             var p = this.node.parentNode;
32423             if(p){
32424                 indent = p.ui.getChildIndent();
32425             }
32426             if(this.indentMarkup != indent){ // don't rerender if not required
32427                 this.indentNode.innerHTML = indent;
32428                 this.indentMarkup = indent;
32429             }
32430             this.updateExpandIcon();
32431         }
32432     }
32433 };
32434
32435 Roo.tree.RootTreeNodeUI = function(){
32436     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32437 };
32438 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32439     render : function(){
32440         if(!this.rendered){
32441             var targetNode = this.node.ownerTree.innerCt.dom;
32442             this.node.expanded = true;
32443             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32444             this.wrap = this.ctNode = targetNode.firstChild;
32445         }
32446     },
32447     collapse : function(){
32448     },
32449     expand : function(){
32450     }
32451 });/*
32452  * Based on:
32453  * Ext JS Library 1.1.1
32454  * Copyright(c) 2006-2007, Ext JS, LLC.
32455  *
32456  * Originally Released Under LGPL - original licence link has changed is not relivant.
32457  *
32458  * Fork - LGPL
32459  * <script type="text/javascript">
32460  */
32461 /**
32462  * @class Roo.tree.TreeLoader
32463  * @extends Roo.util.Observable
32464  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32465  * nodes from a specified URL. The response must be a javascript Array definition
32466  * who's elements are node definition objects. eg:
32467  * <pre><code>
32468    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32469     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32470 </code></pre>
32471  * <br><br>
32472  * A server request is sent, and child nodes are loaded only when a node is expanded.
32473  * The loading node's id is passed to the server under the parameter name "node" to
32474  * enable the server to produce the correct child nodes.
32475  * <br><br>
32476  * To pass extra parameters, an event handler may be attached to the "beforeload"
32477  * event, and the parameters specified in the TreeLoader's baseParams property:
32478  * <pre><code>
32479     myTreeLoader.on("beforeload", function(treeLoader, node) {
32480         this.baseParams.category = node.attributes.category;
32481     }, this);
32482 </code></pre><
32483  * This would pass an HTTP parameter called "category" to the server containing
32484  * the value of the Node's "category" attribute.
32485  * @constructor
32486  * Creates a new Treeloader.
32487  * @param {Object} config A config object containing config properties.
32488  */
32489 Roo.tree.TreeLoader = function(config){
32490     this.baseParams = {};
32491     this.requestMethod = "POST";
32492     Roo.apply(this, config);
32493
32494     this.addEvents({
32495     
32496         /**
32497          * @event beforeload
32498          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32499          * @param {Object} This TreeLoader object.
32500          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32501          * @param {Object} callback The callback function specified in the {@link #load} call.
32502          */
32503         beforeload : true,
32504         /**
32505          * @event load
32506          * Fires when the node has been successfuly loaded.
32507          * @param {Object} This TreeLoader object.
32508          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32509          * @param {Object} response The response object containing the data from the server.
32510          */
32511         load : true,
32512         /**
32513          * @event loadexception
32514          * Fires if the network request failed.
32515          * @param {Object} This TreeLoader object.
32516          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32517          * @param {Object} response The response object containing the data from the server.
32518          */
32519         loadexception : true,
32520         /**
32521          * @event create
32522          * Fires before a node is created, enabling you to return custom Node types 
32523          * @param {Object} This TreeLoader object.
32524          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32525          */
32526         create : true
32527     });
32528
32529     Roo.tree.TreeLoader.superclass.constructor.call(this);
32530 };
32531
32532 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32533     /**
32534     * @cfg {String} dataUrl The URL from which to request a Json string which
32535     * specifies an array of node definition object representing the child nodes
32536     * to be loaded.
32537     */
32538     /**
32539     * @cfg {Object} baseParams (optional) An object containing properties which
32540     * specify HTTP parameters to be passed to each request for child nodes.
32541     */
32542     /**
32543     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32544     * created by this loader. If the attributes sent by the server have an attribute in this object,
32545     * they take priority.
32546     */
32547     /**
32548     * @cfg {Object} uiProviders (optional) An object containing properties which
32549     * 
32550     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32551     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32552     * <i>uiProvider</i> attribute of a returned child node is a string rather
32553     * than a reference to a TreeNodeUI implementation, this that string value
32554     * is used as a property name in the uiProviders object. You can define the provider named
32555     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32556     */
32557     uiProviders : {},
32558
32559     /**
32560     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32561     * child nodes before loading.
32562     */
32563     clearOnLoad : true,
32564
32565     /**
32566     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32567     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32568     * Grid query { data : [ .....] }
32569     */
32570     
32571     root : false,
32572      /**
32573     * @cfg {String} queryParam (optional) 
32574     * Name of the query as it will be passed on the querystring (defaults to 'node')
32575     * eg. the request will be ?node=[id]
32576     */
32577     
32578     
32579     queryParam: false,
32580     
32581     /**
32582      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32583      * This is called automatically when a node is expanded, but may be used to reload
32584      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32585      * @param {Roo.tree.TreeNode} node
32586      * @param {Function} callback
32587      */
32588     load : function(node, callback){
32589         if(this.clearOnLoad){
32590             while(node.firstChild){
32591                 node.removeChild(node.firstChild);
32592             }
32593         }
32594         if(node.attributes.children){ // preloaded json children
32595             var cs = node.attributes.children;
32596             for(var i = 0, len = cs.length; i < len; i++){
32597                 node.appendChild(this.createNode(cs[i]));
32598             }
32599             if(typeof callback == "function"){
32600                 callback();
32601             }
32602         }else if(this.dataUrl){
32603             this.requestData(node, callback);
32604         }
32605     },
32606
32607     getParams: function(node){
32608         var buf = [], bp = this.baseParams;
32609         for(var key in bp){
32610             if(typeof bp[key] != "function"){
32611                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32612             }
32613         }
32614         var n = this.queryParam === false ? 'node' : this.queryParam;
32615         buf.push(n + "=", encodeURIComponent(node.id));
32616         return buf.join("");
32617     },
32618
32619     requestData : function(node, callback){
32620         if(this.fireEvent("beforeload", this, node, callback) !== false){
32621             this.transId = Roo.Ajax.request({
32622                 method:this.requestMethod,
32623                 url: this.dataUrl||this.url,
32624                 success: this.handleResponse,
32625                 failure: this.handleFailure,
32626                 scope: this,
32627                 argument: {callback: callback, node: node},
32628                 params: this.getParams(node)
32629             });
32630         }else{
32631             // if the load is cancelled, make sure we notify
32632             // the node that we are done
32633             if(typeof callback == "function"){
32634                 callback();
32635             }
32636         }
32637     },
32638
32639     isLoading : function(){
32640         return this.transId ? true : false;
32641     },
32642
32643     abort : function(){
32644         if(this.isLoading()){
32645             Roo.Ajax.abort(this.transId);
32646         }
32647     },
32648
32649     // private
32650     createNode : function(attr){
32651         // apply baseAttrs, nice idea Corey!
32652         if(this.baseAttrs){
32653             Roo.applyIf(attr, this.baseAttrs);
32654         }
32655         if(this.applyLoader !== false){
32656             attr.loader = this;
32657         }
32658         // uiProvider = depreciated..
32659         
32660         if(typeof(attr.uiProvider) == 'string'){
32661            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32662                 /**  eval:var:attr */ eval(attr.uiProvider);
32663         }
32664         if(typeof(this.uiProviders['default']) != 'undefined') {
32665             attr.uiProvider = this.uiProviders['default'];
32666         }
32667         
32668         this.fireEvent('create', this, attr);
32669         
32670         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32671         return(attr.leaf ?
32672                         new Roo.tree.TreeNode(attr) :
32673                         new Roo.tree.AsyncTreeNode(attr));
32674     },
32675
32676     processResponse : function(response, node, callback){
32677         var json = response.responseText;
32678         try {
32679             
32680             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32681             if (this.root !== false) {
32682                 o = o[this.root];
32683             }
32684             
32685             for(var i = 0, len = o.length; i < len; i++){
32686                 var n = this.createNode(o[i]);
32687                 if(n){
32688                     node.appendChild(n);
32689                 }
32690             }
32691             if(typeof callback == "function"){
32692                 callback(this, node);
32693             }
32694         }catch(e){
32695             this.handleFailure(response);
32696         }
32697     },
32698
32699     handleResponse : function(response){
32700         this.transId = false;
32701         var a = response.argument;
32702         this.processResponse(response, a.node, a.callback);
32703         this.fireEvent("load", this, a.node, response);
32704     },
32705
32706     handleFailure : function(response){
32707         this.transId = false;
32708         var a = response.argument;
32709         this.fireEvent("loadexception", this, a.node, response);
32710         if(typeof a.callback == "function"){
32711             a.callback(this, a.node);
32712         }
32713     }
32714 });/*
32715  * Based on:
32716  * Ext JS Library 1.1.1
32717  * Copyright(c) 2006-2007, Ext JS, LLC.
32718  *
32719  * Originally Released Under LGPL - original licence link has changed is not relivant.
32720  *
32721  * Fork - LGPL
32722  * <script type="text/javascript">
32723  */
32724
32725 /**
32726 * @class Roo.tree.TreeFilter
32727 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32728 * @param {TreePanel} tree
32729 * @param {Object} config (optional)
32730  */
32731 Roo.tree.TreeFilter = function(tree, config){
32732     this.tree = tree;
32733     this.filtered = {};
32734     Roo.apply(this, config);
32735 };
32736
32737 Roo.tree.TreeFilter.prototype = {
32738     clearBlank:false,
32739     reverse:false,
32740     autoClear:false,
32741     remove:false,
32742
32743      /**
32744      * Filter the data by a specific attribute.
32745      * @param {String/RegExp} value Either string that the attribute value
32746      * should start with or a RegExp to test against the attribute
32747      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32748      * @param {TreeNode} startNode (optional) The node to start the filter at.
32749      */
32750     filter : function(value, attr, startNode){
32751         attr = attr || "text";
32752         var f;
32753         if(typeof value == "string"){
32754             var vlen = value.length;
32755             // auto clear empty filter
32756             if(vlen == 0 && this.clearBlank){
32757                 this.clear();
32758                 return;
32759             }
32760             value = value.toLowerCase();
32761             f = function(n){
32762                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32763             };
32764         }else if(value.exec){ // regex?
32765             f = function(n){
32766                 return value.test(n.attributes[attr]);
32767             };
32768         }else{
32769             throw 'Illegal filter type, must be string or regex';
32770         }
32771         this.filterBy(f, null, startNode);
32772         },
32773
32774     /**
32775      * Filter by a function. The passed function will be called with each
32776      * node in the tree (or from the startNode). If the function returns true, the node is kept
32777      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32778      * @param {Function} fn The filter function
32779      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32780      */
32781     filterBy : function(fn, scope, startNode){
32782         startNode = startNode || this.tree.root;
32783         if(this.autoClear){
32784             this.clear();
32785         }
32786         var af = this.filtered, rv = this.reverse;
32787         var f = function(n){
32788             if(n == startNode){
32789                 return true;
32790             }
32791             if(af[n.id]){
32792                 return false;
32793             }
32794             var m = fn.call(scope || n, n);
32795             if(!m || rv){
32796                 af[n.id] = n;
32797                 n.ui.hide();
32798                 return false;
32799             }
32800             return true;
32801         };
32802         startNode.cascade(f);
32803         if(this.remove){
32804            for(var id in af){
32805                if(typeof id != "function"){
32806                    var n = af[id];
32807                    if(n && n.parentNode){
32808                        n.parentNode.removeChild(n);
32809                    }
32810                }
32811            }
32812         }
32813     },
32814
32815     /**
32816      * Clears the current filter. Note: with the "remove" option
32817      * set a filter cannot be cleared.
32818      */
32819     clear : function(){
32820         var t = this.tree;
32821         var af = this.filtered;
32822         for(var id in af){
32823             if(typeof id != "function"){
32824                 var n = af[id];
32825                 if(n){
32826                     n.ui.show();
32827                 }
32828             }
32829         }
32830         this.filtered = {};
32831     }
32832 };
32833 /*
32834  * Based on:
32835  * Ext JS Library 1.1.1
32836  * Copyright(c) 2006-2007, Ext JS, LLC.
32837  *
32838  * Originally Released Under LGPL - original licence link has changed is not relivant.
32839  *
32840  * Fork - LGPL
32841  * <script type="text/javascript">
32842  */
32843  
32844
32845 /**
32846  * @class Roo.tree.TreeSorter
32847  * Provides sorting of nodes in a TreePanel
32848  * 
32849  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32850  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32851  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32852  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32853  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32854  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32855  * @constructor
32856  * @param {TreePanel} tree
32857  * @param {Object} config
32858  */
32859 Roo.tree.TreeSorter = function(tree, config){
32860     Roo.apply(this, config);
32861     tree.on("beforechildrenrendered", this.doSort, this);
32862     tree.on("append", this.updateSort, this);
32863     tree.on("insert", this.updateSort, this);
32864     
32865     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32866     var p = this.property || "text";
32867     var sortType = this.sortType;
32868     var fs = this.folderSort;
32869     var cs = this.caseSensitive === true;
32870     var leafAttr = this.leafAttr || 'leaf';
32871
32872     this.sortFn = function(n1, n2){
32873         if(fs){
32874             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32875                 return 1;
32876             }
32877             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32878                 return -1;
32879             }
32880         }
32881         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32882         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32883         if(v1 < v2){
32884                         return dsc ? +1 : -1;
32885                 }else if(v1 > v2){
32886                         return dsc ? -1 : +1;
32887         }else{
32888                 return 0;
32889         }
32890     };
32891 };
32892
32893 Roo.tree.TreeSorter.prototype = {
32894     doSort : function(node){
32895         node.sort(this.sortFn);
32896     },
32897     
32898     compareNodes : function(n1, n2){
32899         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32900     },
32901     
32902     updateSort : function(tree, node){
32903         if(node.childrenRendered){
32904             this.doSort.defer(1, this, [node]);
32905         }
32906     }
32907 };/*
32908  * Based on:
32909  * Ext JS Library 1.1.1
32910  * Copyright(c) 2006-2007, Ext JS, LLC.
32911  *
32912  * Originally Released Under LGPL - original licence link has changed is not relivant.
32913  *
32914  * Fork - LGPL
32915  * <script type="text/javascript">
32916  */
32917
32918 if(Roo.dd.DropZone){
32919     
32920 Roo.tree.TreeDropZone = function(tree, config){
32921     this.allowParentInsert = false;
32922     this.allowContainerDrop = false;
32923     this.appendOnly = false;
32924     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32925     this.tree = tree;
32926     this.lastInsertClass = "x-tree-no-status";
32927     this.dragOverData = {};
32928 };
32929
32930 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32931     ddGroup : "TreeDD",
32932     
32933     expandDelay : 1000,
32934     
32935     expandNode : function(node){
32936         if(node.hasChildNodes() && !node.isExpanded()){
32937             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32938         }
32939     },
32940     
32941     queueExpand : function(node){
32942         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32943     },
32944     
32945     cancelExpand : function(){
32946         if(this.expandProcId){
32947             clearTimeout(this.expandProcId);
32948             this.expandProcId = false;
32949         }
32950     },
32951     
32952     isValidDropPoint : function(n, pt, dd, e, data){
32953         if(!n || !data){ return false; }
32954         var targetNode = n.node;
32955         var dropNode = data.node;
32956         // default drop rules
32957         if(!(targetNode && targetNode.isTarget && pt)){
32958             return false;
32959         }
32960         if(pt == "append" && targetNode.allowChildren === false){
32961             return false;
32962         }
32963         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32964             return false;
32965         }
32966         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32967             return false;
32968         }
32969         // reuse the object
32970         var overEvent = this.dragOverData;
32971         overEvent.tree = this.tree;
32972         overEvent.target = targetNode;
32973         overEvent.data = data;
32974         overEvent.point = pt;
32975         overEvent.source = dd;
32976         overEvent.rawEvent = e;
32977         overEvent.dropNode = dropNode;
32978         overEvent.cancel = false;  
32979         var result = this.tree.fireEvent("nodedragover", overEvent);
32980         return overEvent.cancel === false && result !== false;
32981     },
32982     
32983     getDropPoint : function(e, n, dd){
32984         var tn = n.node;
32985         if(tn.isRoot){
32986             return tn.allowChildren !== false ? "append" : false; // always append for root
32987         }
32988         var dragEl = n.ddel;
32989         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32990         var y = Roo.lib.Event.getPageY(e);
32991         //var noAppend = tn.allowChildren === false || tn.isLeaf();
32992         
32993         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
32994         var noAppend = tn.allowChildren === false;
32995         if(this.appendOnly || tn.parentNode.allowChildren === false){
32996             return noAppend ? false : "append";
32997         }
32998         var noBelow = false;
32999         if(!this.allowParentInsert){
33000             noBelow = tn.hasChildNodes() && tn.isExpanded();
33001         }
33002         var q = (b - t) / (noAppend ? 2 : 3);
33003         if(y >= t && y < (t + q)){
33004             return "above";
33005         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33006             return "below";
33007         }else{
33008             return "append";
33009         }
33010     },
33011     
33012     onNodeEnter : function(n, dd, e, data){
33013         this.cancelExpand();
33014     },
33015     
33016     onNodeOver : function(n, dd, e, data){
33017         var pt = this.getDropPoint(e, n, dd);
33018         var node = n.node;
33019         
33020         // auto node expand check
33021         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33022             this.queueExpand(node);
33023         }else if(pt != "append"){
33024             this.cancelExpand();
33025         }
33026         
33027         // set the insert point style on the target node
33028         var returnCls = this.dropNotAllowed;
33029         if(this.isValidDropPoint(n, pt, dd, e, data)){
33030            if(pt){
33031                var el = n.ddel;
33032                var cls;
33033                if(pt == "above"){
33034                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33035                    cls = "x-tree-drag-insert-above";
33036                }else if(pt == "below"){
33037                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33038                    cls = "x-tree-drag-insert-below";
33039                }else{
33040                    returnCls = "x-tree-drop-ok-append";
33041                    cls = "x-tree-drag-append";
33042                }
33043                if(this.lastInsertClass != cls){
33044                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33045                    this.lastInsertClass = cls;
33046                }
33047            }
33048        }
33049        return returnCls;
33050     },
33051     
33052     onNodeOut : function(n, dd, e, data){
33053         this.cancelExpand();
33054         this.removeDropIndicators(n);
33055     },
33056     
33057     onNodeDrop : function(n, dd, e, data){
33058         var point = this.getDropPoint(e, n, dd);
33059         var targetNode = n.node;
33060         targetNode.ui.startDrop();
33061         if(!this.isValidDropPoint(n, point, dd, e, data)){
33062             targetNode.ui.endDrop();
33063             return false;
33064         }
33065         // first try to find the drop node
33066         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33067         var dropEvent = {
33068             tree : this.tree,
33069             target: targetNode,
33070             data: data,
33071             point: point,
33072             source: dd,
33073             rawEvent: e,
33074             dropNode: dropNode,
33075             cancel: !dropNode   
33076         };
33077         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33078         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33079             targetNode.ui.endDrop();
33080             return false;
33081         }
33082         // allow target changing
33083         targetNode = dropEvent.target;
33084         if(point == "append" && !targetNode.isExpanded()){
33085             targetNode.expand(false, null, function(){
33086                 this.completeDrop(dropEvent);
33087             }.createDelegate(this));
33088         }else{
33089             this.completeDrop(dropEvent);
33090         }
33091         return true;
33092     },
33093     
33094     completeDrop : function(de){
33095         var ns = de.dropNode, p = de.point, t = de.target;
33096         if(!(ns instanceof Array)){
33097             ns = [ns];
33098         }
33099         var n;
33100         for(var i = 0, len = ns.length; i < len; i++){
33101             n = ns[i];
33102             if(p == "above"){
33103                 t.parentNode.insertBefore(n, t);
33104             }else if(p == "below"){
33105                 t.parentNode.insertBefore(n, t.nextSibling);
33106             }else{
33107                 t.appendChild(n);
33108             }
33109         }
33110         n.ui.focus();
33111         if(this.tree.hlDrop){
33112             n.ui.highlight();
33113         }
33114         t.ui.endDrop();
33115         this.tree.fireEvent("nodedrop", de);
33116     },
33117     
33118     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33119         if(this.tree.hlDrop){
33120             dropNode.ui.focus();
33121             dropNode.ui.highlight();
33122         }
33123         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33124     },
33125     
33126     getTree : function(){
33127         return this.tree;
33128     },
33129     
33130     removeDropIndicators : function(n){
33131         if(n && n.ddel){
33132             var el = n.ddel;
33133             Roo.fly(el).removeClass([
33134                     "x-tree-drag-insert-above",
33135                     "x-tree-drag-insert-below",
33136                     "x-tree-drag-append"]);
33137             this.lastInsertClass = "_noclass";
33138         }
33139     },
33140     
33141     beforeDragDrop : function(target, e, id){
33142         this.cancelExpand();
33143         return true;
33144     },
33145     
33146     afterRepair : function(data){
33147         if(data && Roo.enableFx){
33148             data.node.ui.highlight();
33149         }
33150         this.hideProxy();
33151     }    
33152 });
33153
33154 }
33155 /*
33156  * Based on:
33157  * Ext JS Library 1.1.1
33158  * Copyright(c) 2006-2007, Ext JS, LLC.
33159  *
33160  * Originally Released Under LGPL - original licence link has changed is not relivant.
33161  *
33162  * Fork - LGPL
33163  * <script type="text/javascript">
33164  */
33165  
33166
33167 if(Roo.dd.DragZone){
33168 Roo.tree.TreeDragZone = function(tree, config){
33169     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33170     this.tree = tree;
33171 };
33172
33173 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33174     ddGroup : "TreeDD",
33175     
33176     onBeforeDrag : function(data, e){
33177         var n = data.node;
33178         return n && n.draggable && !n.disabled;
33179     },
33180     
33181     onInitDrag : function(e){
33182         var data = this.dragData;
33183         this.tree.getSelectionModel().select(data.node);
33184         this.proxy.update("");
33185         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33186         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33187     },
33188     
33189     getRepairXY : function(e, data){
33190         return data.node.ui.getDDRepairXY();
33191     },
33192     
33193     onEndDrag : function(data, e){
33194         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33195     },
33196     
33197     onValidDrop : function(dd, e, id){
33198         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33199         this.hideProxy();
33200     },
33201     
33202     beforeInvalidDrop : function(e, id){
33203         // this scrolls the original position back into view
33204         var sm = this.tree.getSelectionModel();
33205         sm.clearSelections();
33206         sm.select(this.dragData.node);
33207     }
33208 });
33209 }/*
33210  * Based on:
33211  * Ext JS Library 1.1.1
33212  * Copyright(c) 2006-2007, Ext JS, LLC.
33213  *
33214  * Originally Released Under LGPL - original licence link has changed is not relivant.
33215  *
33216  * Fork - LGPL
33217  * <script type="text/javascript">
33218  */
33219 /**
33220  * @class Roo.tree.TreeEditor
33221  * @extends Roo.Editor
33222  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33223  * as the editor field.
33224  * @constructor
33225  * @param {TreePanel} tree
33226  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33227  */
33228 Roo.tree.TreeEditor = function(tree, config){
33229     config = config || {};
33230     var field = config.events ? config : new Roo.form.TextField(config);
33231     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33232
33233     this.tree = tree;
33234
33235     tree.on('beforeclick', this.beforeNodeClick, this);
33236     tree.getTreeEl().on('mousedown', this.hide, this);
33237     this.on('complete', this.updateNode, this);
33238     this.on('beforestartedit', this.fitToTree, this);
33239     this.on('startedit', this.bindScroll, this, {delay:10});
33240     this.on('specialkey', this.onSpecialKey, this);
33241 };
33242
33243 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33244     /**
33245      * @cfg {String} alignment
33246      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33247      */
33248     alignment: "l-l",
33249     // inherit
33250     autoSize: false,
33251     /**
33252      * @cfg {Boolean} hideEl
33253      * True to hide the bound element while the editor is displayed (defaults to false)
33254      */
33255     hideEl : false,
33256     /**
33257      * @cfg {String} cls
33258      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33259      */
33260     cls: "x-small-editor x-tree-editor",
33261     /**
33262      * @cfg {Boolean} shim
33263      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33264      */
33265     shim:false,
33266     // inherit
33267     shadow:"frame",
33268     /**
33269      * @cfg {Number} maxWidth
33270      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33271      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33272      * scroll and client offsets into account prior to each edit.
33273      */
33274     maxWidth: 250,
33275
33276     editDelay : 350,
33277
33278     // private
33279     fitToTree : function(ed, el){
33280         var td = this.tree.getTreeEl().dom, nd = el.dom;
33281         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33282             td.scrollLeft = nd.offsetLeft;
33283         }
33284         var w = Math.min(
33285                 this.maxWidth,
33286                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33287         this.setSize(w, '');
33288     },
33289
33290     // private
33291     triggerEdit : function(node){
33292         this.completeEdit();
33293         this.editNode = node;
33294         this.startEdit(node.ui.textNode, node.text);
33295     },
33296
33297     // private
33298     bindScroll : function(){
33299         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33300     },
33301
33302     // private
33303     beforeNodeClick : function(node, e){
33304         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33305         this.lastClick = new Date();
33306         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33307             e.stopEvent();
33308             this.triggerEdit(node);
33309             return false;
33310         }
33311     },
33312
33313     // private
33314     updateNode : function(ed, value){
33315         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33316         this.editNode.setText(value);
33317     },
33318
33319     // private
33320     onHide : function(){
33321         Roo.tree.TreeEditor.superclass.onHide.call(this);
33322         if(this.editNode){
33323             this.editNode.ui.focus();
33324         }
33325     },
33326
33327     // private
33328     onSpecialKey : function(field, e){
33329         var k = e.getKey();
33330         if(k == e.ESC){
33331             e.stopEvent();
33332             this.cancelEdit();
33333         }else if(k == e.ENTER && !e.hasModifier()){
33334             e.stopEvent();
33335             this.completeEdit();
33336         }
33337     }
33338 });//<Script type="text/javascript">
33339 /*
33340  * Based on:
33341  * Ext JS Library 1.1.1
33342  * Copyright(c) 2006-2007, Ext JS, LLC.
33343  *
33344  * Originally Released Under LGPL - original licence link has changed is not relivant.
33345  *
33346  * Fork - LGPL
33347  * <script type="text/javascript">
33348  */
33349  
33350 /**
33351  * Not documented??? - probably should be...
33352  */
33353
33354 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33355     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33356     
33357     renderElements : function(n, a, targetNode, bulkRender){
33358         //consel.log("renderElements?");
33359         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33360
33361         var t = n.getOwnerTree();
33362         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33363         
33364         var cols = t.columns;
33365         var bw = t.borderWidth;
33366         var c = cols[0];
33367         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33368          var cb = typeof a.checked == "boolean";
33369         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33370         var colcls = 'x-t-' + tid + '-c0';
33371         var buf = [
33372             '<li class="x-tree-node">',
33373             
33374                 
33375                 '<div class="x-tree-node-el ', a.cls,'">',
33376                     // extran...
33377                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33378                 
33379                 
33380                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33381                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33382                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33383                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33384                            (a.iconCls ? ' '+a.iconCls : ''),
33385                            '" unselectable="on" />',
33386                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33387                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33388                              
33389                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33390                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33391                             '<span unselectable="on" qtip="' + tx + '">',
33392                              tx,
33393                              '</span></a>' ,
33394                     '</div>',
33395                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33396                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33397                  ];
33398         for(var i = 1, len = cols.length; i < len; i++){
33399             c = cols[i];
33400             colcls = 'x-t-' + tid + '-c' +i;
33401             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33402             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33403                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33404                       "</div>");
33405          }
33406          
33407          buf.push(
33408             '</a>',
33409             '<div class="x-clear"></div></div>',
33410             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33411             "</li>");
33412         
33413         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33414             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33415                                 n.nextSibling.ui.getEl(), buf.join(""));
33416         }else{
33417             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33418         }
33419         var el = this.wrap.firstChild;
33420         this.elRow = el;
33421         this.elNode = el.firstChild;
33422         this.ranchor = el.childNodes[1];
33423         this.ctNode = this.wrap.childNodes[1];
33424         var cs = el.firstChild.childNodes;
33425         this.indentNode = cs[0];
33426         this.ecNode = cs[1];
33427         this.iconNode = cs[2];
33428         var index = 3;
33429         if(cb){
33430             this.checkbox = cs[3];
33431             index++;
33432         }
33433         this.anchor = cs[index];
33434         
33435         this.textNode = cs[index].firstChild;
33436         
33437         //el.on("click", this.onClick, this);
33438         //el.on("dblclick", this.onDblClick, this);
33439         
33440         
33441        // console.log(this);
33442     },
33443     initEvents : function(){
33444         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33445         
33446             
33447         var a = this.ranchor;
33448
33449         var el = Roo.get(a);
33450
33451         if(Roo.isOpera){ // opera render bug ignores the CSS
33452             el.setStyle("text-decoration", "none");
33453         }
33454
33455         el.on("click", this.onClick, this);
33456         el.on("dblclick", this.onDblClick, this);
33457         el.on("contextmenu", this.onContextMenu, this);
33458         
33459     },
33460     
33461     /*onSelectedChange : function(state){
33462         if(state){
33463             this.focus();
33464             this.addClass("x-tree-selected");
33465         }else{
33466             //this.blur();
33467             this.removeClass("x-tree-selected");
33468         }
33469     },*/
33470     addClass : function(cls){
33471         if(this.elRow){
33472             Roo.fly(this.elRow).addClass(cls);
33473         }
33474         
33475     },
33476     
33477     
33478     removeClass : function(cls){
33479         if(this.elRow){
33480             Roo.fly(this.elRow).removeClass(cls);
33481         }
33482     }
33483
33484     
33485     
33486 });//<Script type="text/javascript">
33487
33488 /*
33489  * Based on:
33490  * Ext JS Library 1.1.1
33491  * Copyright(c) 2006-2007, Ext JS, LLC.
33492  *
33493  * Originally Released Under LGPL - original licence link has changed is not relivant.
33494  *
33495  * Fork - LGPL
33496  * <script type="text/javascript">
33497  */
33498  
33499
33500 /**
33501  * @class Roo.tree.ColumnTree
33502  * @extends Roo.data.TreePanel
33503  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33504  * @cfg {int} borderWidth  compined right/left border allowance
33505  * @constructor
33506  * @param {String/HTMLElement/Element} el The container element
33507  * @param {Object} config
33508  */
33509 Roo.tree.ColumnTree =  function(el, config)
33510 {
33511    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33512    this.addEvents({
33513         /**
33514         * @event resize
33515         * Fire this event on a container when it resizes
33516         * @param {int} w Width
33517         * @param {int} h Height
33518         */
33519        "resize" : true
33520     });
33521     this.on('resize', this.onResize, this);
33522 };
33523
33524 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33525     //lines:false,
33526     
33527     
33528     borderWidth: Roo.isBorderBox ? 0 : 2, 
33529     headEls : false,
33530     
33531     render : function(){
33532         // add the header.....
33533        
33534         Roo.tree.ColumnTree.superclass.render.apply(this);
33535         
33536         this.el.addClass('x-column-tree');
33537         
33538         this.headers = this.el.createChild(
33539             {cls:'x-tree-headers'},this.innerCt.dom);
33540    
33541         var cols = this.columns, c;
33542         var totalWidth = 0;
33543         this.headEls = [];
33544         var  len = cols.length;
33545         for(var i = 0; i < len; i++){
33546              c = cols[i];
33547              totalWidth += c.width;
33548             this.headEls.push(this.headers.createChild({
33549                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33550                  cn: {
33551                      cls:'x-tree-hd-text',
33552                      html: c.header
33553                  },
33554                  style:'width:'+(c.width-this.borderWidth)+'px;'
33555              }));
33556         }
33557         this.headers.createChild({cls:'x-clear'});
33558         // prevent floats from wrapping when clipped
33559         this.headers.setWidth(totalWidth);
33560         //this.innerCt.setWidth(totalWidth);
33561         this.innerCt.setStyle({ overflow: 'auto' });
33562         this.onResize(this.width, this.height);
33563              
33564         
33565     },
33566     onResize : function(w,h)
33567     {
33568         this.height = h;
33569         this.width = w;
33570         // resize cols..
33571         this.innerCt.setWidth(this.width);
33572         this.innerCt.setHeight(this.height-20);
33573         
33574         // headers...
33575         var cols = this.columns, c;
33576         var totalWidth = 0;
33577         var expEl = false;
33578         var len = cols.length;
33579         for(var i = 0; i < len; i++){
33580             c = cols[i];
33581             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33582                 // it's the expander..
33583                 expEl  = this.headEls[i];
33584                 continue;
33585             }
33586             totalWidth += c.width;
33587             
33588         }
33589         if (expEl) {
33590             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33591         }
33592         this.headers.setWidth(w-20);
33593
33594         
33595         
33596         
33597     }
33598 });
33599 /*
33600  * Based on:
33601  * Ext JS Library 1.1.1
33602  * Copyright(c) 2006-2007, Ext JS, LLC.
33603  *
33604  * Originally Released Under LGPL - original licence link has changed is not relivant.
33605  *
33606  * Fork - LGPL
33607  * <script type="text/javascript">
33608  */
33609  
33610 /**
33611  * @class Roo.menu.Menu
33612  * @extends Roo.util.Observable
33613  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33614  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33615  * @constructor
33616  * Creates a new Menu
33617  * @param {Object} config Configuration options
33618  */
33619 Roo.menu.Menu = function(config){
33620     Roo.apply(this, config);
33621     this.id = this.id || Roo.id();
33622     this.addEvents({
33623         /**
33624          * @event beforeshow
33625          * Fires before this menu is displayed
33626          * @param {Roo.menu.Menu} this
33627          */
33628         beforeshow : true,
33629         /**
33630          * @event beforehide
33631          * Fires before this menu is hidden
33632          * @param {Roo.menu.Menu} this
33633          */
33634         beforehide : true,
33635         /**
33636          * @event show
33637          * Fires after this menu is displayed
33638          * @param {Roo.menu.Menu} this
33639          */
33640         show : true,
33641         /**
33642          * @event hide
33643          * Fires after this menu is hidden
33644          * @param {Roo.menu.Menu} this
33645          */
33646         hide : true,
33647         /**
33648          * @event click
33649          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33650          * @param {Roo.menu.Menu} this
33651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33652          * @param {Roo.EventObject} e
33653          */
33654         click : true,
33655         /**
33656          * @event mouseover
33657          * Fires when the mouse is hovering over this menu
33658          * @param {Roo.menu.Menu} this
33659          * @param {Roo.EventObject} e
33660          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33661          */
33662         mouseover : true,
33663         /**
33664          * @event mouseout
33665          * Fires when the mouse exits this menu
33666          * @param {Roo.menu.Menu} this
33667          * @param {Roo.EventObject} e
33668          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33669          */
33670         mouseout : true,
33671         /**
33672          * @event itemclick
33673          * Fires when a menu item contained in this menu is clicked
33674          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33675          * @param {Roo.EventObject} e
33676          */
33677         itemclick: true
33678     });
33679     if (this.registerMenu) {
33680         Roo.menu.MenuMgr.register(this);
33681     }
33682     
33683     var mis = this.items;
33684     this.items = new Roo.util.MixedCollection();
33685     if(mis){
33686         this.add.apply(this, mis);
33687     }
33688 };
33689
33690 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33691     /**
33692      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33693      */
33694     minWidth : 120,
33695     /**
33696      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33697      * for bottom-right shadow (defaults to "sides")
33698      */
33699     shadow : "sides",
33700     /**
33701      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33702      * this menu (defaults to "tl-tr?")
33703      */
33704     subMenuAlign : "tl-tr?",
33705     /**
33706      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33707      * relative to its element of origin (defaults to "tl-bl?")
33708      */
33709     defaultAlign : "tl-bl?",
33710     /**
33711      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33712      */
33713     allowOtherMenus : false,
33714     /**
33715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33716      */
33717     registerMenu : true,
33718
33719     hidden:true,
33720
33721     // private
33722     render : function(){
33723         if(this.el){
33724             return;
33725         }
33726         var el = this.el = new Roo.Layer({
33727             cls: "x-menu",
33728             shadow:this.shadow,
33729             constrain: false,
33730             parentEl: this.parentEl || document.body,
33731             zindex:15000
33732         });
33733
33734         this.keyNav = new Roo.menu.MenuNav(this);
33735
33736         if(this.plain){
33737             el.addClass("x-menu-plain");
33738         }
33739         if(this.cls){
33740             el.addClass(this.cls);
33741         }
33742         // generic focus element
33743         this.focusEl = el.createChild({
33744             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33745         });
33746         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33747         ul.on("click", this.onClick, this);
33748         ul.on("mouseover", this.onMouseOver, this);
33749         ul.on("mouseout", this.onMouseOut, this);
33750         this.items.each(function(item){
33751             var li = document.createElement("li");
33752             li.className = "x-menu-list-item";
33753             ul.dom.appendChild(li);
33754             item.render(li, this);
33755         }, this);
33756         this.ul = ul;
33757         this.autoWidth();
33758     },
33759
33760     // private
33761     autoWidth : function(){
33762         var el = this.el, ul = this.ul;
33763         if(!el){
33764             return;
33765         }
33766         var w = this.width;
33767         if(w){
33768             el.setWidth(w);
33769         }else if(Roo.isIE){
33770             el.setWidth(this.minWidth);
33771             var t = el.dom.offsetWidth; // force recalc
33772             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33773         }
33774     },
33775
33776     // private
33777     delayAutoWidth : function(){
33778         if(this.rendered){
33779             if(!this.awTask){
33780                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33781             }
33782             this.awTask.delay(20);
33783         }
33784     },
33785
33786     // private
33787     findTargetItem : function(e){
33788         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33789         if(t && t.menuItemId){
33790             return this.items.get(t.menuItemId);
33791         }
33792     },
33793
33794     // private
33795     onClick : function(e){
33796         var t;
33797         if(t = this.findTargetItem(e)){
33798             t.onClick(e);
33799             this.fireEvent("click", this, t, e);
33800         }
33801     },
33802
33803     // private
33804     setActiveItem : function(item, autoExpand){
33805         if(item != this.activeItem){
33806             if(this.activeItem){
33807                 this.activeItem.deactivate();
33808             }
33809             this.activeItem = item;
33810             item.activate(autoExpand);
33811         }else if(autoExpand){
33812             item.expandMenu();
33813         }
33814     },
33815
33816     // private
33817     tryActivate : function(start, step){
33818         var items = this.items;
33819         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33820             var item = items.get(i);
33821             if(!item.disabled && item.canActivate){
33822                 this.setActiveItem(item, false);
33823                 return item;
33824             }
33825         }
33826         return false;
33827     },
33828
33829     // private
33830     onMouseOver : function(e){
33831         var t;
33832         if(t = this.findTargetItem(e)){
33833             if(t.canActivate && !t.disabled){
33834                 this.setActiveItem(t, true);
33835             }
33836         }
33837         this.fireEvent("mouseover", this, e, t);
33838     },
33839
33840     // private
33841     onMouseOut : function(e){
33842         var t;
33843         if(t = this.findTargetItem(e)){
33844             if(t == this.activeItem && t.shouldDeactivate(e)){
33845                 this.activeItem.deactivate();
33846                 delete this.activeItem;
33847             }
33848         }
33849         this.fireEvent("mouseout", this, e, t);
33850     },
33851
33852     /**
33853      * Read-only.  Returns true if the menu is currently displayed, else false.
33854      * @type Boolean
33855      */
33856     isVisible : function(){
33857         return this.el && !this.hidden;
33858     },
33859
33860     /**
33861      * Displays this menu relative to another element
33862      * @param {String/HTMLElement/Roo.Element} element The element to align to
33863      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33864      * the element (defaults to this.defaultAlign)
33865      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33866      */
33867     show : function(el, pos, parentMenu){
33868         this.parentMenu = parentMenu;
33869         if(!this.el){
33870             this.render();
33871         }
33872         this.fireEvent("beforeshow", this);
33873         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33874     },
33875
33876     /**
33877      * Displays this menu at a specific xy position
33878      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33879      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33880      */
33881     showAt : function(xy, parentMenu, /* private: */_e){
33882         this.parentMenu = parentMenu;
33883         if(!this.el){
33884             this.render();
33885         }
33886         if(_e !== false){
33887             this.fireEvent("beforeshow", this);
33888             xy = this.el.adjustForConstraints(xy);
33889         }
33890         this.el.setXY(xy);
33891         this.el.show();
33892         this.hidden = false;
33893         this.focus();
33894         this.fireEvent("show", this);
33895     },
33896
33897     focus : function(){
33898         if(!this.hidden){
33899             this.doFocus.defer(50, this);
33900         }
33901     },
33902
33903     doFocus : function(){
33904         if(!this.hidden){
33905             this.focusEl.focus();
33906         }
33907     },
33908
33909     /**
33910      * Hides this menu and optionally all parent menus
33911      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33912      */
33913     hide : function(deep){
33914         if(this.el && this.isVisible()){
33915             this.fireEvent("beforehide", this);
33916             if(this.activeItem){
33917                 this.activeItem.deactivate();
33918                 this.activeItem = null;
33919             }
33920             this.el.hide();
33921             this.hidden = true;
33922             this.fireEvent("hide", this);
33923         }
33924         if(deep === true && this.parentMenu){
33925             this.parentMenu.hide(true);
33926         }
33927     },
33928
33929     /**
33930      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33931      * Any of the following are valid:
33932      * <ul>
33933      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33934      * <li>An HTMLElement object which will be converted to a menu item</li>
33935      * <li>A menu item config object that will be created as a new menu item</li>
33936      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33937      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33938      * </ul>
33939      * Usage:
33940      * <pre><code>
33941 // Create the menu
33942 var menu = new Roo.menu.Menu();
33943
33944 // Create a menu item to add by reference
33945 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33946
33947 // Add a bunch of items at once using different methods.
33948 // Only the last item added will be returned.
33949 var item = menu.add(
33950     menuItem,                // add existing item by ref
33951     'Dynamic Item',          // new TextItem
33952     '-',                     // new separator
33953     { text: 'Config Item' }  // new item by config
33954 );
33955 </code></pre>
33956      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33957      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33958      */
33959     add : function(){
33960         var a = arguments, l = a.length, item;
33961         for(var i = 0; i < l; i++){
33962             var el = a[i];
33963             if ((typeof(el) == "object") && el.xtype && el.xns) {
33964                 el = Roo.factory(el, Roo.menu);
33965             }
33966             
33967             if(el.render){ // some kind of Item
33968                 item = this.addItem(el);
33969             }else if(typeof el == "string"){ // string
33970                 if(el == "separator" || el == "-"){
33971                     item = this.addSeparator();
33972                 }else{
33973                     item = this.addText(el);
33974                 }
33975             }else if(el.tagName || el.el){ // element
33976                 item = this.addElement(el);
33977             }else if(typeof el == "object"){ // must be menu item config?
33978                 item = this.addMenuItem(el);
33979             }
33980         }
33981         return item;
33982     },
33983
33984     /**
33985      * Returns this menu's underlying {@link Roo.Element} object
33986      * @return {Roo.Element} The element
33987      */
33988     getEl : function(){
33989         if(!this.el){
33990             this.render();
33991         }
33992         return this.el;
33993     },
33994
33995     /**
33996      * Adds a separator bar to the menu
33997      * @return {Roo.menu.Item} The menu item that was added
33998      */
33999     addSeparator : function(){
34000         return this.addItem(new Roo.menu.Separator());
34001     },
34002
34003     /**
34004      * Adds an {@link Roo.Element} object to the menu
34005      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34006      * @return {Roo.menu.Item} The menu item that was added
34007      */
34008     addElement : function(el){
34009         return this.addItem(new Roo.menu.BaseItem(el));
34010     },
34011
34012     /**
34013      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34014      * @param {Roo.menu.Item} item The menu item to add
34015      * @return {Roo.menu.Item} The menu item that was added
34016      */
34017     addItem : function(item){
34018         this.items.add(item);
34019         if(this.ul){
34020             var li = document.createElement("li");
34021             li.className = "x-menu-list-item";
34022             this.ul.dom.appendChild(li);
34023             item.render(li, this);
34024             this.delayAutoWidth();
34025         }
34026         return item;
34027     },
34028
34029     /**
34030      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34031      * @param {Object} config A MenuItem config object
34032      * @return {Roo.menu.Item} The menu item that was added
34033      */
34034     addMenuItem : function(config){
34035         if(!(config instanceof Roo.menu.Item)){
34036             if(typeof config.checked == "boolean"){ // must be check menu item config?
34037                 config = new Roo.menu.CheckItem(config);
34038             }else{
34039                 config = new Roo.menu.Item(config);
34040             }
34041         }
34042         return this.addItem(config);
34043     },
34044
34045     /**
34046      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34047      * @param {String} text The text to display in the menu item
34048      * @return {Roo.menu.Item} The menu item that was added
34049      */
34050     addText : function(text){
34051         return this.addItem(new Roo.menu.TextItem({ text : text }));
34052     },
34053
34054     /**
34055      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34056      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34057      * @param {Roo.menu.Item} item The menu item to add
34058      * @return {Roo.menu.Item} The menu item that was added
34059      */
34060     insert : function(index, item){
34061         this.items.insert(index, item);
34062         if(this.ul){
34063             var li = document.createElement("li");
34064             li.className = "x-menu-list-item";
34065             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34066             item.render(li, this);
34067             this.delayAutoWidth();
34068         }
34069         return item;
34070     },
34071
34072     /**
34073      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34074      * @param {Roo.menu.Item} item The menu item to remove
34075      */
34076     remove : function(item){
34077         this.items.removeKey(item.id);
34078         item.destroy();
34079     },
34080
34081     /**
34082      * Removes and destroys all items in the menu
34083      */
34084     removeAll : function(){
34085         var f;
34086         while(f = this.items.first()){
34087             this.remove(f);
34088         }
34089     }
34090 });
34091
34092 // MenuNav is a private utility class used internally by the Menu
34093 Roo.menu.MenuNav = function(menu){
34094     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34095     this.scope = this.menu = menu;
34096 };
34097
34098 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34099     doRelay : function(e, h){
34100         var k = e.getKey();
34101         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34102             this.menu.tryActivate(0, 1);
34103             return false;
34104         }
34105         return h.call(this.scope || this, e, this.menu);
34106     },
34107
34108     up : function(e, m){
34109         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34110             m.tryActivate(m.items.length-1, -1);
34111         }
34112     },
34113
34114     down : function(e, m){
34115         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34116             m.tryActivate(0, 1);
34117         }
34118     },
34119
34120     right : function(e, m){
34121         if(m.activeItem){
34122             m.activeItem.expandMenu(true);
34123         }
34124     },
34125
34126     left : function(e, m){
34127         m.hide();
34128         if(m.parentMenu && m.parentMenu.activeItem){
34129             m.parentMenu.activeItem.activate();
34130         }
34131     },
34132
34133     enter : function(e, m){
34134         if(m.activeItem){
34135             e.stopPropagation();
34136             m.activeItem.onClick(e);
34137             m.fireEvent("click", this, m.activeItem);
34138             return true;
34139         }
34140     }
34141 });/*
34142  * Based on:
34143  * Ext JS Library 1.1.1
34144  * Copyright(c) 2006-2007, Ext JS, LLC.
34145  *
34146  * Originally Released Under LGPL - original licence link has changed is not relivant.
34147  *
34148  * Fork - LGPL
34149  * <script type="text/javascript">
34150  */
34151  
34152 /**
34153  * @class Roo.menu.MenuMgr
34154  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34155  * @singleton
34156  */
34157 Roo.menu.MenuMgr = function(){
34158    var menus, active, groups = {}, attached = false, lastShow = new Date();
34159
34160    // private - called when first menu is created
34161    function init(){
34162        menus = {};
34163        active = new Roo.util.MixedCollection();
34164        Roo.get(document).addKeyListener(27, function(){
34165            if(active.length > 0){
34166                hideAll();
34167            }
34168        });
34169    }
34170
34171    // private
34172    function hideAll(){
34173        if(active && active.length > 0){
34174            var c = active.clone();
34175            c.each(function(m){
34176                m.hide();
34177            });
34178        }
34179    }
34180
34181    // private
34182    function onHide(m){
34183        active.remove(m);
34184        if(active.length < 1){
34185            Roo.get(document).un("mousedown", onMouseDown);
34186            attached = false;
34187        }
34188    }
34189
34190    // private
34191    function onShow(m){
34192        var last = active.last();
34193        lastShow = new Date();
34194        active.add(m);
34195        if(!attached){
34196            Roo.get(document).on("mousedown", onMouseDown);
34197            attached = true;
34198        }
34199        if(m.parentMenu){
34200           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34201           m.parentMenu.activeChild = m;
34202        }else if(last && last.isVisible()){
34203           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34204        }
34205    }
34206
34207    // private
34208    function onBeforeHide(m){
34209        if(m.activeChild){
34210            m.activeChild.hide();
34211        }
34212        if(m.autoHideTimer){
34213            clearTimeout(m.autoHideTimer);
34214            delete m.autoHideTimer;
34215        }
34216    }
34217
34218    // private
34219    function onBeforeShow(m){
34220        var pm = m.parentMenu;
34221        if(!pm && !m.allowOtherMenus){
34222            hideAll();
34223        }else if(pm && pm.activeChild && active != m){
34224            pm.activeChild.hide();
34225        }
34226    }
34227
34228    // private
34229    function onMouseDown(e){
34230        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34231            hideAll();
34232        }
34233    }
34234
34235    // private
34236    function onBeforeCheck(mi, state){
34237        if(state){
34238            var g = groups[mi.group];
34239            for(var i = 0, l = g.length; i < l; i++){
34240                if(g[i] != mi){
34241                    g[i].setChecked(false);
34242                }
34243            }
34244        }
34245    }
34246
34247    return {
34248
34249        /**
34250         * Hides all menus that are currently visible
34251         */
34252        hideAll : function(){
34253             hideAll();  
34254        },
34255
34256        // private
34257        register : function(menu){
34258            if(!menus){
34259                init();
34260            }
34261            menus[menu.id] = menu;
34262            menu.on("beforehide", onBeforeHide);
34263            menu.on("hide", onHide);
34264            menu.on("beforeshow", onBeforeShow);
34265            menu.on("show", onShow);
34266            var g = menu.group;
34267            if(g && menu.events["checkchange"]){
34268                if(!groups[g]){
34269                    groups[g] = [];
34270                }
34271                groups[g].push(menu);
34272                menu.on("checkchange", onCheck);
34273            }
34274        },
34275
34276         /**
34277          * Returns a {@link Roo.menu.Menu} object
34278          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34279          * be used to generate and return a new Menu instance.
34280          */
34281        get : function(menu){
34282            if(typeof menu == "string"){ // menu id
34283                return menus[menu];
34284            }else if(menu.events){  // menu instance
34285                return menu;
34286            }else if(typeof menu.length == 'number'){ // array of menu items?
34287                return new Roo.menu.Menu({items:menu});
34288            }else{ // otherwise, must be a config
34289                return new Roo.menu.Menu(menu);
34290            }
34291        },
34292
34293        // private
34294        unregister : function(menu){
34295            delete menus[menu.id];
34296            menu.un("beforehide", onBeforeHide);
34297            menu.un("hide", onHide);
34298            menu.un("beforeshow", onBeforeShow);
34299            menu.un("show", onShow);
34300            var g = menu.group;
34301            if(g && menu.events["checkchange"]){
34302                groups[g].remove(menu);
34303                menu.un("checkchange", onCheck);
34304            }
34305        },
34306
34307        // private
34308        registerCheckable : function(menuItem){
34309            var g = menuItem.group;
34310            if(g){
34311                if(!groups[g]){
34312                    groups[g] = [];
34313                }
34314                groups[g].push(menuItem);
34315                menuItem.on("beforecheckchange", onBeforeCheck);
34316            }
34317        },
34318
34319        // private
34320        unregisterCheckable : function(menuItem){
34321            var g = menuItem.group;
34322            if(g){
34323                groups[g].remove(menuItem);
34324                menuItem.un("beforecheckchange", onBeforeCheck);
34325            }
34326        }
34327    };
34328 }();/*
34329  * Based on:
34330  * Ext JS Library 1.1.1
34331  * Copyright(c) 2006-2007, Ext JS, LLC.
34332  *
34333  * Originally Released Under LGPL - original licence link has changed is not relivant.
34334  *
34335  * Fork - LGPL
34336  * <script type="text/javascript">
34337  */
34338  
34339
34340 /**
34341  * @class Roo.menu.BaseItem
34342  * @extends Roo.Component
34343  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34344  * management and base configuration options shared by all menu components.
34345  * @constructor
34346  * Creates a new BaseItem
34347  * @param {Object} config Configuration options
34348  */
34349 Roo.menu.BaseItem = function(config){
34350     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34351
34352     this.addEvents({
34353         /**
34354          * @event click
34355          * Fires when this item is clicked
34356          * @param {Roo.menu.BaseItem} this
34357          * @param {Roo.EventObject} e
34358          */
34359         click: true,
34360         /**
34361          * @event activate
34362          * Fires when this item is activated
34363          * @param {Roo.menu.BaseItem} this
34364          */
34365         activate : true,
34366         /**
34367          * @event deactivate
34368          * Fires when this item is deactivated
34369          * @param {Roo.menu.BaseItem} this
34370          */
34371         deactivate : true
34372     });
34373
34374     if(this.handler){
34375         this.on("click", this.handler, this.scope, true);
34376     }
34377 };
34378
34379 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34380     /**
34381      * @cfg {Function} handler
34382      * A function that will handle the click event of this menu item (defaults to undefined)
34383      */
34384     /**
34385      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34386      */
34387     canActivate : false,
34388     /**
34389      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34390      */
34391     activeClass : "x-menu-item-active",
34392     /**
34393      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34394      */
34395     hideOnClick : true,
34396     /**
34397      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34398      */
34399     hideDelay : 100,
34400
34401     // private
34402     ctype: "Roo.menu.BaseItem",
34403
34404     // private
34405     actionMode : "container",
34406
34407     // private
34408     render : function(container, parentMenu){
34409         this.parentMenu = parentMenu;
34410         Roo.menu.BaseItem.superclass.render.call(this, container);
34411         this.container.menuItemId = this.id;
34412     },
34413
34414     // private
34415     onRender : function(container, position){
34416         this.el = Roo.get(this.el);
34417         container.dom.appendChild(this.el.dom);
34418     },
34419
34420     // private
34421     onClick : function(e){
34422         if(!this.disabled && this.fireEvent("click", this, e) !== false
34423                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34424             this.handleClick(e);
34425         }else{
34426             e.stopEvent();
34427         }
34428     },
34429
34430     // private
34431     activate : function(){
34432         if(this.disabled){
34433             return false;
34434         }
34435         var li = this.container;
34436         li.addClass(this.activeClass);
34437         this.region = li.getRegion().adjust(2, 2, -2, -2);
34438         this.fireEvent("activate", this);
34439         return true;
34440     },
34441
34442     // private
34443     deactivate : function(){
34444         this.container.removeClass(this.activeClass);
34445         this.fireEvent("deactivate", this);
34446     },
34447
34448     // private
34449     shouldDeactivate : function(e){
34450         return !this.region || !this.region.contains(e.getPoint());
34451     },
34452
34453     // private
34454     handleClick : function(e){
34455         if(this.hideOnClick){
34456             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34457         }
34458     },
34459
34460     // private
34461     expandMenu : function(autoActivate){
34462         // do nothing
34463     },
34464
34465     // private
34466     hideMenu : function(){
34467         // do nothing
34468     }
34469 });/*
34470  * Based on:
34471  * Ext JS Library 1.1.1
34472  * Copyright(c) 2006-2007, Ext JS, LLC.
34473  *
34474  * Originally Released Under LGPL - original licence link has changed is not relivant.
34475  *
34476  * Fork - LGPL
34477  * <script type="text/javascript">
34478  */
34479  
34480 /**
34481  * @class Roo.menu.Adapter
34482  * @extends Roo.menu.BaseItem
34483  * 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.
34484  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34485  * @constructor
34486  * Creates a new Adapter
34487  * @param {Object} config Configuration options
34488  */
34489 Roo.menu.Adapter = function(component, config){
34490     Roo.menu.Adapter.superclass.constructor.call(this, config);
34491     this.component = component;
34492 };
34493 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34494     // private
34495     canActivate : true,
34496
34497     // private
34498     onRender : function(container, position){
34499         this.component.render(container);
34500         this.el = this.component.getEl();
34501     },
34502
34503     // private
34504     activate : function(){
34505         if(this.disabled){
34506             return false;
34507         }
34508         this.component.focus();
34509         this.fireEvent("activate", this);
34510         return true;
34511     },
34512
34513     // private
34514     deactivate : function(){
34515         this.fireEvent("deactivate", this);
34516     },
34517
34518     // private
34519     disable : function(){
34520         this.component.disable();
34521         Roo.menu.Adapter.superclass.disable.call(this);
34522     },
34523
34524     // private
34525     enable : function(){
34526         this.component.enable();
34527         Roo.menu.Adapter.superclass.enable.call(this);
34528     }
34529 });/*
34530  * Based on:
34531  * Ext JS Library 1.1.1
34532  * Copyright(c) 2006-2007, Ext JS, LLC.
34533  *
34534  * Originally Released Under LGPL - original licence link has changed is not relivant.
34535  *
34536  * Fork - LGPL
34537  * <script type="text/javascript">
34538  */
34539
34540 /**
34541  * @class Roo.menu.TextItem
34542  * @extends Roo.menu.BaseItem
34543  * Adds a static text string to a menu, usually used as either a heading or group separator.
34544  * Note: old style constructor with text is still supported.
34545  * 
34546  * @constructor
34547  * Creates a new TextItem
34548  * @param {Object} cfg Configuration
34549  */
34550 Roo.menu.TextItem = function(cfg){
34551     if (typeof(cfg) == 'string') {
34552         this.text = cfg;
34553     } else {
34554         Roo.apply(this,cfg);
34555     }
34556     
34557     Roo.menu.TextItem.superclass.constructor.call(this);
34558 };
34559
34560 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34561     /**
34562      * @cfg {Boolean} text Text to show on item.
34563      */
34564     text : '',
34565     
34566     /**
34567      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34568      */
34569     hideOnClick : false,
34570     /**
34571      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34572      */
34573     itemCls : "x-menu-text",
34574
34575     // private
34576     onRender : function(){
34577         var s = document.createElement("span");
34578         s.className = this.itemCls;
34579         s.innerHTML = this.text;
34580         this.el = s;
34581         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34582     }
34583 });/*
34584  * Based on:
34585  * Ext JS Library 1.1.1
34586  * Copyright(c) 2006-2007, Ext JS, LLC.
34587  *
34588  * Originally Released Under LGPL - original licence link has changed is not relivant.
34589  *
34590  * Fork - LGPL
34591  * <script type="text/javascript">
34592  */
34593
34594 /**
34595  * @class Roo.menu.Separator
34596  * @extends Roo.menu.BaseItem
34597  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34598  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34599  * @constructor
34600  * @param {Object} config Configuration options
34601  */
34602 Roo.menu.Separator = function(config){
34603     Roo.menu.Separator.superclass.constructor.call(this, config);
34604 };
34605
34606 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34607     /**
34608      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34609      */
34610     itemCls : "x-menu-sep",
34611     /**
34612      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34613      */
34614     hideOnClick : false,
34615
34616     // private
34617     onRender : function(li){
34618         var s = document.createElement("span");
34619         s.className = this.itemCls;
34620         s.innerHTML = "&#160;";
34621         this.el = s;
34622         li.addClass("x-menu-sep-li");
34623         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34624     }
34625 });/*
34626  * Based on:
34627  * Ext JS Library 1.1.1
34628  * Copyright(c) 2006-2007, Ext JS, LLC.
34629  *
34630  * Originally Released Under LGPL - original licence link has changed is not relivant.
34631  *
34632  * Fork - LGPL
34633  * <script type="text/javascript">
34634  */
34635 /**
34636  * @class Roo.menu.Item
34637  * @extends Roo.menu.BaseItem
34638  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34639  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34640  * activation and click handling.
34641  * @constructor
34642  * Creates a new Item
34643  * @param {Object} config Configuration options
34644  */
34645 Roo.menu.Item = function(config){
34646     Roo.menu.Item.superclass.constructor.call(this, config);
34647     if(this.menu){
34648         this.menu = Roo.menu.MenuMgr.get(this.menu);
34649     }
34650 };
34651 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34652     
34653     /**
34654      * @cfg {String} text
34655      * The text to show on the menu item.
34656      */
34657     text: '',
34658      /**
34659      * @cfg {String} HTML to render in menu
34660      * The text to show on the menu item (HTML version).
34661      */
34662     html: '',
34663     /**
34664      * @cfg {String} icon
34665      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34666      */
34667     icon: undefined,
34668     /**
34669      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34670      */
34671     itemCls : "x-menu-item",
34672     /**
34673      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34674      */
34675     canActivate : true,
34676     /**
34677      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34678      */
34679     showDelay: 200,
34680     // doc'd in BaseItem
34681     hideDelay: 200,
34682
34683     // private
34684     ctype: "Roo.menu.Item",
34685     
34686     // private
34687     onRender : function(container, position){
34688         var el = document.createElement("a");
34689         el.hideFocus = true;
34690         el.unselectable = "on";
34691         el.href = this.href || "#";
34692         if(this.hrefTarget){
34693             el.target = this.hrefTarget;
34694         }
34695         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34696         
34697         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34698         
34699         el.innerHTML = String.format(
34700                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34701                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34702         this.el = el;
34703         Roo.menu.Item.superclass.onRender.call(this, container, position);
34704     },
34705
34706     /**
34707      * Sets the text to display in this menu item
34708      * @param {String} text The text to display
34709      * @param {Boolean} isHTML true to indicate text is pure html.
34710      */
34711     setText : function(text, isHTML){
34712         if (isHTML) {
34713             this.html = text;
34714         } else {
34715             this.text = text;
34716             this.html = '';
34717         }
34718         if(this.rendered){
34719             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34720      
34721             this.el.update(String.format(
34722                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34723                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34724             this.parentMenu.autoWidth();
34725         }
34726     },
34727
34728     // private
34729     handleClick : function(e){
34730         if(!this.href){ // if no link defined, stop the event automatically
34731             e.stopEvent();
34732         }
34733         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34734     },
34735
34736     // private
34737     activate : function(autoExpand){
34738         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34739             this.focus();
34740             if(autoExpand){
34741                 this.expandMenu();
34742             }
34743         }
34744         return true;
34745     },
34746
34747     // private
34748     shouldDeactivate : function(e){
34749         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34750             if(this.menu && this.menu.isVisible()){
34751                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34752             }
34753             return true;
34754         }
34755         return false;
34756     },
34757
34758     // private
34759     deactivate : function(){
34760         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34761         this.hideMenu();
34762     },
34763
34764     // private
34765     expandMenu : function(autoActivate){
34766         if(!this.disabled && this.menu){
34767             clearTimeout(this.hideTimer);
34768             delete this.hideTimer;
34769             if(!this.menu.isVisible() && !this.showTimer){
34770                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34771             }else if (this.menu.isVisible() && autoActivate){
34772                 this.menu.tryActivate(0, 1);
34773             }
34774         }
34775     },
34776
34777     // private
34778     deferExpand : function(autoActivate){
34779         delete this.showTimer;
34780         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34781         if(autoActivate){
34782             this.menu.tryActivate(0, 1);
34783         }
34784     },
34785
34786     // private
34787     hideMenu : function(){
34788         clearTimeout(this.showTimer);
34789         delete this.showTimer;
34790         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34791             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34792         }
34793     },
34794
34795     // private
34796     deferHide : function(){
34797         delete this.hideTimer;
34798         this.menu.hide();
34799     }
34800 });/*
34801  * Based on:
34802  * Ext JS Library 1.1.1
34803  * Copyright(c) 2006-2007, Ext JS, LLC.
34804  *
34805  * Originally Released Under LGPL - original licence link has changed is not relivant.
34806  *
34807  * Fork - LGPL
34808  * <script type="text/javascript">
34809  */
34810  
34811 /**
34812  * @class Roo.menu.CheckItem
34813  * @extends Roo.menu.Item
34814  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34815  * @constructor
34816  * Creates a new CheckItem
34817  * @param {Object} config Configuration options
34818  */
34819 Roo.menu.CheckItem = function(config){
34820     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34821     this.addEvents({
34822         /**
34823          * @event beforecheckchange
34824          * Fires before the checked value is set, providing an opportunity to cancel if needed
34825          * @param {Roo.menu.CheckItem} this
34826          * @param {Boolean} checked The new checked value that will be set
34827          */
34828         "beforecheckchange" : true,
34829         /**
34830          * @event checkchange
34831          * Fires after the checked value has been set
34832          * @param {Roo.menu.CheckItem} this
34833          * @param {Boolean} checked The checked value that was set
34834          */
34835         "checkchange" : true
34836     });
34837     if(this.checkHandler){
34838         this.on('checkchange', this.checkHandler, this.scope);
34839     }
34840 };
34841 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34842     /**
34843      * @cfg {String} group
34844      * All check items with the same group name will automatically be grouped into a single-select
34845      * radio button group (defaults to '')
34846      */
34847     /**
34848      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34849      */
34850     itemCls : "x-menu-item x-menu-check-item",
34851     /**
34852      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34853      */
34854     groupClass : "x-menu-group-item",
34855
34856     /**
34857      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34858      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34859      * initialized with checked = true will be rendered as checked.
34860      */
34861     checked: false,
34862
34863     // private
34864     ctype: "Roo.menu.CheckItem",
34865
34866     // private
34867     onRender : function(c){
34868         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34869         if(this.group){
34870             this.el.addClass(this.groupClass);
34871         }
34872         Roo.menu.MenuMgr.registerCheckable(this);
34873         if(this.checked){
34874             this.checked = false;
34875             this.setChecked(true, true);
34876         }
34877     },
34878
34879     // private
34880     destroy : function(){
34881         if(this.rendered){
34882             Roo.menu.MenuMgr.unregisterCheckable(this);
34883         }
34884         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34885     },
34886
34887     /**
34888      * Set the checked state of this item
34889      * @param {Boolean} checked The new checked value
34890      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34891      */
34892     setChecked : function(state, suppressEvent){
34893         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34894             if(this.container){
34895                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34896             }
34897             this.checked = state;
34898             if(suppressEvent !== true){
34899                 this.fireEvent("checkchange", this, state);
34900             }
34901         }
34902     },
34903
34904     // private
34905     handleClick : function(e){
34906        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34907            this.setChecked(!this.checked);
34908        }
34909        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34910     }
34911 });/*
34912  * Based on:
34913  * Ext JS Library 1.1.1
34914  * Copyright(c) 2006-2007, Ext JS, LLC.
34915  *
34916  * Originally Released Under LGPL - original licence link has changed is not relivant.
34917  *
34918  * Fork - LGPL
34919  * <script type="text/javascript">
34920  */
34921  
34922 /**
34923  * @class Roo.menu.DateItem
34924  * @extends Roo.menu.Adapter
34925  * A menu item that wraps the {@link Roo.DatPicker} component.
34926  * @constructor
34927  * Creates a new DateItem
34928  * @param {Object} config Configuration options
34929  */
34930 Roo.menu.DateItem = function(config){
34931     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34932     /** The Roo.DatePicker object @type Roo.DatePicker */
34933     this.picker = this.component;
34934     this.addEvents({select: true});
34935     
34936     this.picker.on("render", function(picker){
34937         picker.getEl().swallowEvent("click");
34938         picker.container.addClass("x-menu-date-item");
34939     });
34940
34941     this.picker.on("select", this.onSelect, this);
34942 };
34943
34944 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34945     // private
34946     onSelect : function(picker, date){
34947         this.fireEvent("select", this, date, picker);
34948         Roo.menu.DateItem.superclass.handleClick.call(this);
34949     }
34950 });/*
34951  * Based on:
34952  * Ext JS Library 1.1.1
34953  * Copyright(c) 2006-2007, Ext JS, LLC.
34954  *
34955  * Originally Released Under LGPL - original licence link has changed is not relivant.
34956  *
34957  * Fork - LGPL
34958  * <script type="text/javascript">
34959  */
34960  
34961 /**
34962  * @class Roo.menu.ColorItem
34963  * @extends Roo.menu.Adapter
34964  * A menu item that wraps the {@link Roo.ColorPalette} component.
34965  * @constructor
34966  * Creates a new ColorItem
34967  * @param {Object} config Configuration options
34968  */
34969 Roo.menu.ColorItem = function(config){
34970     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34971     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34972     this.palette = this.component;
34973     this.relayEvents(this.palette, ["select"]);
34974     if(this.selectHandler){
34975         this.on('select', this.selectHandler, this.scope);
34976     }
34977 };
34978 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34979  * Based on:
34980  * Ext JS Library 1.1.1
34981  * Copyright(c) 2006-2007, Ext JS, LLC.
34982  *
34983  * Originally Released Under LGPL - original licence link has changed is not relivant.
34984  *
34985  * Fork - LGPL
34986  * <script type="text/javascript">
34987  */
34988  
34989
34990 /**
34991  * @class Roo.menu.DateMenu
34992  * @extends Roo.menu.Menu
34993  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34994  * @constructor
34995  * Creates a new DateMenu
34996  * @param {Object} config Configuration options
34997  */
34998 Roo.menu.DateMenu = function(config){
34999     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35000     this.plain = true;
35001     var di = new Roo.menu.DateItem(config);
35002     this.add(di);
35003     /**
35004      * The {@link Roo.DatePicker} instance for this DateMenu
35005      * @type DatePicker
35006      */
35007     this.picker = di.picker;
35008     /**
35009      * @event select
35010      * @param {DatePicker} picker
35011      * @param {Date} date
35012      */
35013     this.relayEvents(di, ["select"]);
35014
35015     this.on('beforeshow', function(){
35016         if(this.picker){
35017             this.picker.hideMonthPicker(true);
35018         }
35019     }, this);
35020 };
35021 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35022     cls:'x-date-menu'
35023 });/*
35024  * Based on:
35025  * Ext JS Library 1.1.1
35026  * Copyright(c) 2006-2007, Ext JS, LLC.
35027  *
35028  * Originally Released Under LGPL - original licence link has changed is not relivant.
35029  *
35030  * Fork - LGPL
35031  * <script type="text/javascript">
35032  */
35033  
35034
35035 /**
35036  * @class Roo.menu.ColorMenu
35037  * @extends Roo.menu.Menu
35038  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35039  * @constructor
35040  * Creates a new ColorMenu
35041  * @param {Object} config Configuration options
35042  */
35043 Roo.menu.ColorMenu = function(config){
35044     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35045     this.plain = true;
35046     var ci = new Roo.menu.ColorItem(config);
35047     this.add(ci);
35048     /**
35049      * The {@link Roo.ColorPalette} instance for this ColorMenu
35050      * @type ColorPalette
35051      */
35052     this.palette = ci.palette;
35053     /**
35054      * @event select
35055      * @param {ColorPalette} palette
35056      * @param {String} color
35057      */
35058     this.relayEvents(ci, ["select"]);
35059 };
35060 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35061  * Based on:
35062  * Ext JS Library 1.1.1
35063  * Copyright(c) 2006-2007, Ext JS, LLC.
35064  *
35065  * Originally Released Under LGPL - original licence link has changed is not relivant.
35066  *
35067  * Fork - LGPL
35068  * <script type="text/javascript">
35069  */
35070  
35071 /**
35072  * @class Roo.form.Field
35073  * @extends Roo.BoxComponent
35074  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35075  * @constructor
35076  * Creates a new Field
35077  * @param {Object} config Configuration options
35078  */
35079 Roo.form.Field = function(config){
35080     Roo.form.Field.superclass.constructor.call(this, config);
35081 };
35082
35083 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35084     /**
35085      * @cfg {String} fieldLabel Label to use when rendering a form.
35086      */
35087        /**
35088      * @cfg {String} qtip Mouse over tip
35089      */
35090      
35091     /**
35092      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35093      */
35094     invalidClass : "x-form-invalid",
35095     /**
35096      * @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")
35097      */
35098     invalidText : "The value in this field is invalid",
35099     /**
35100      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35101      */
35102     focusClass : "x-form-focus",
35103     /**
35104      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35105       automatic validation (defaults to "keyup").
35106      */
35107     validationEvent : "keyup",
35108     /**
35109      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35110      */
35111     validateOnBlur : true,
35112     /**
35113      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35114      */
35115     validationDelay : 250,
35116     /**
35117      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35118      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35119      */
35120     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35121     /**
35122      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35123      */
35124     fieldClass : "x-form-field",
35125     /**
35126      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35127      *<pre>
35128 Value         Description
35129 -----------   ----------------------------------------------------------------------
35130 qtip          Display a quick tip when the user hovers over the field
35131 title         Display a default browser title attribute popup
35132 under         Add a block div beneath the field containing the error text
35133 side          Add an error icon to the right of the field with a popup on hover
35134 [element id]  Add the error text directly to the innerHTML of the specified element
35135 </pre>
35136      */
35137     msgTarget : 'qtip',
35138     /**
35139      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35140      */
35141     msgFx : 'normal',
35142
35143     /**
35144      * @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.
35145      */
35146     readOnly : false,
35147
35148     /**
35149      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35150      */
35151     disabled : false,
35152
35153     /**
35154      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35155      */
35156     inputType : undefined,
35157     
35158     /**
35159      * @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).
35160          */
35161         tabIndex : undefined,
35162         
35163     // private
35164     isFormField : true,
35165
35166     // private
35167     hasFocus : false,
35168     /**
35169      * @property {Roo.Element} fieldEl
35170      * Element Containing the rendered Field (with label etc.)
35171      */
35172     /**
35173      * @cfg {Mixed} value A value to initialize this field with.
35174      */
35175     value : undefined,
35176
35177     /**
35178      * @cfg {String} name The field's HTML name attribute.
35179      */
35180     /**
35181      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35182      */
35183
35184         // private ??
35185         initComponent : function(){
35186         Roo.form.Field.superclass.initComponent.call(this);
35187         this.addEvents({
35188             /**
35189              * @event focus
35190              * Fires when this field receives input focus.
35191              * @param {Roo.form.Field} this
35192              */
35193             focus : true,
35194             /**
35195              * @event blur
35196              * Fires when this field loses input focus.
35197              * @param {Roo.form.Field} this
35198              */
35199             blur : true,
35200             /**
35201              * @event specialkey
35202              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35203              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35204              * @param {Roo.form.Field} this
35205              * @param {Roo.EventObject} e The event object
35206              */
35207             specialkey : true,
35208             /**
35209              * @event change
35210              * Fires just before the field blurs if the field value has changed.
35211              * @param {Roo.form.Field} this
35212              * @param {Mixed} newValue The new value
35213              * @param {Mixed} oldValue The original value
35214              */
35215             change : true,
35216             /**
35217              * @event invalid
35218              * Fires after the field has been marked as invalid.
35219              * @param {Roo.form.Field} this
35220              * @param {String} msg The validation message
35221              */
35222             invalid : true,
35223             /**
35224              * @event valid
35225              * Fires after the field has been validated with no errors.
35226              * @param {Roo.form.Field} this
35227              */
35228             valid : true,
35229              /**
35230              * @event keyup
35231              * Fires after the key up
35232              * @param {Roo.form.Field} this
35233              * @param {Roo.EventObject}  e The event Object
35234              */
35235             keyup : true
35236         });
35237     },
35238
35239     /**
35240      * Returns the name attribute of the field if available
35241      * @return {String} name The field name
35242      */
35243     getName: function(){
35244          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35245     },
35246
35247     // private
35248     onRender : function(ct, position){
35249         Roo.form.Field.superclass.onRender.call(this, ct, position);
35250         if(!this.el){
35251             var cfg = this.getAutoCreate();
35252             if(!cfg.name){
35253                 cfg.name = this.name || this.id;
35254             }
35255             if(this.inputType){
35256                 cfg.type = this.inputType;
35257             }
35258             this.el = ct.createChild(cfg, position);
35259         }
35260         var type = this.el.dom.type;
35261         if(type){
35262             if(type == 'password'){
35263                 type = 'text';
35264             }
35265             this.el.addClass('x-form-'+type);
35266         }
35267         if(this.readOnly){
35268             this.el.dom.readOnly = true;
35269         }
35270         if(this.tabIndex !== undefined){
35271             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35272         }
35273
35274         this.el.addClass([this.fieldClass, this.cls]);
35275         this.initValue();
35276     },
35277
35278     /**
35279      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35280      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35281      * @return {Roo.form.Field} this
35282      */
35283     applyTo : function(target){
35284         this.allowDomMove = false;
35285         this.el = Roo.get(target);
35286         this.render(this.el.dom.parentNode);
35287         return this;
35288     },
35289
35290     // private
35291     initValue : function(){
35292         if(this.value !== undefined){
35293             this.setValue(this.value);
35294         }else if(this.el.dom.value.length > 0){
35295             this.setValue(this.el.dom.value);
35296         }
35297     },
35298
35299     /**
35300      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35301      */
35302     isDirty : function() {
35303         if(this.disabled) {
35304             return false;
35305         }
35306         return String(this.getValue()) !== String(this.originalValue);
35307     },
35308
35309     // private
35310     afterRender : function(){
35311         Roo.form.Field.superclass.afterRender.call(this);
35312         this.initEvents();
35313     },
35314
35315     // private
35316     fireKey : function(e){
35317         //Roo.log('field ' + e.getKey());
35318         if(e.isNavKeyPress()){
35319             this.fireEvent("specialkey", this, e);
35320         }
35321     },
35322
35323     /**
35324      * Resets the current field value to the originally loaded value and clears any validation messages
35325      */
35326     reset : function(){
35327         this.setValue(this.originalValue);
35328         this.clearInvalid();
35329     },
35330
35331     // private
35332     initEvents : function(){
35333         // safari killled keypress - so keydown is now used..
35334         this.el.on("keydown" , this.fireKey,  this);
35335         this.el.on("focus", this.onFocus,  this);
35336         this.el.on("blur", this.onBlur,  this);
35337         this.el.relayEvent('keyup', this);
35338
35339         // reference to original value for reset
35340         this.originalValue = this.getValue();
35341     },
35342
35343     // private
35344     onFocus : function(){
35345         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35346             this.el.addClass(this.focusClass);
35347         }
35348         if(!this.hasFocus){
35349             this.hasFocus = true;
35350             this.startValue = this.getValue();
35351             this.fireEvent("focus", this);
35352         }
35353     },
35354
35355     beforeBlur : Roo.emptyFn,
35356
35357     // private
35358     onBlur : function(){
35359         this.beforeBlur();
35360         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35361             this.el.removeClass(this.focusClass);
35362         }
35363         this.hasFocus = false;
35364         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35365             this.validate();
35366         }
35367         var v = this.getValue();
35368         if(String(v) !== String(this.startValue)){
35369             this.fireEvent('change', this, v, this.startValue);
35370         }
35371         this.fireEvent("blur", this);
35372     },
35373
35374     /**
35375      * Returns whether or not the field value is currently valid
35376      * @param {Boolean} preventMark True to disable marking the field invalid
35377      * @return {Boolean} True if the value is valid, else false
35378      */
35379     isValid : function(preventMark){
35380         if(this.disabled){
35381             return true;
35382         }
35383         var restore = this.preventMark;
35384         this.preventMark = preventMark === true;
35385         var v = this.validateValue(this.processValue(this.getRawValue()));
35386         this.preventMark = restore;
35387         return v;
35388     },
35389
35390     /**
35391      * Validates the field value
35392      * @return {Boolean} True if the value is valid, else false
35393      */
35394     validate : function(){
35395         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35396             this.clearInvalid();
35397             return true;
35398         }
35399         return false;
35400     },
35401
35402     processValue : function(value){
35403         return value;
35404     },
35405
35406     // private
35407     // Subclasses should provide the validation implementation by overriding this
35408     validateValue : function(value){
35409         return true;
35410     },
35411
35412     /**
35413      * Mark this field as invalid
35414      * @param {String} msg The validation message
35415      */
35416     markInvalid : function(msg){
35417         if(!this.rendered || this.preventMark){ // not rendered
35418             return;
35419         }
35420         this.el.addClass(this.invalidClass);
35421         msg = msg || this.invalidText;
35422         switch(this.msgTarget){
35423             case 'qtip':
35424                 this.el.dom.qtip = msg;
35425                 this.el.dom.qclass = 'x-form-invalid-tip';
35426                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35427                     Roo.QuickTips.enable();
35428                 }
35429                 break;
35430             case 'title':
35431                 this.el.dom.title = msg;
35432                 break;
35433             case 'under':
35434                 if(!this.errorEl){
35435                     var elp = this.el.findParent('.x-form-element', 5, true);
35436                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35437                     this.errorEl.setWidth(elp.getWidth(true)-20);
35438                 }
35439                 this.errorEl.update(msg);
35440                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35441                 break;
35442             case 'side':
35443                 if(!this.errorIcon){
35444                     var elp = this.el.findParent('.x-form-element', 5, true);
35445                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35446                 }
35447                 this.alignErrorIcon();
35448                 this.errorIcon.dom.qtip = msg;
35449                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35450                 this.errorIcon.show();
35451                 this.on('resize', this.alignErrorIcon, this);
35452                 break;
35453             default:
35454                 var t = Roo.getDom(this.msgTarget);
35455                 t.innerHTML = msg;
35456                 t.style.display = this.msgDisplay;
35457                 break;
35458         }
35459         this.fireEvent('invalid', this, msg);
35460     },
35461
35462     // private
35463     alignErrorIcon : function(){
35464         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35465     },
35466
35467     /**
35468      * Clear any invalid styles/messages for this field
35469      */
35470     clearInvalid : function(){
35471         if(!this.rendered || this.preventMark){ // not rendered
35472             return;
35473         }
35474         this.el.removeClass(this.invalidClass);
35475         switch(this.msgTarget){
35476             case 'qtip':
35477                 this.el.dom.qtip = '';
35478                 break;
35479             case 'title':
35480                 this.el.dom.title = '';
35481                 break;
35482             case 'under':
35483                 if(this.errorEl){
35484                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35485                 }
35486                 break;
35487             case 'side':
35488                 if(this.errorIcon){
35489                     this.errorIcon.dom.qtip = '';
35490                     this.errorIcon.hide();
35491                     this.un('resize', this.alignErrorIcon, this);
35492                 }
35493                 break;
35494             default:
35495                 var t = Roo.getDom(this.msgTarget);
35496                 t.innerHTML = '';
35497                 t.style.display = 'none';
35498                 break;
35499         }
35500         this.fireEvent('valid', this);
35501     },
35502
35503     /**
35504      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35505      * @return {Mixed} value The field value
35506      */
35507     getRawValue : function(){
35508         var v = this.el.getValue();
35509         if(v === this.emptyText){
35510             v = '';
35511         }
35512         return v;
35513     },
35514
35515     /**
35516      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35517      * @return {Mixed} value The field value
35518      */
35519     getValue : function(){
35520         var v = this.el.getValue();
35521         if(v === this.emptyText || v === undefined){
35522             v = '';
35523         }
35524         return v;
35525     },
35526
35527     /**
35528      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35529      * @param {Mixed} value The value to set
35530      */
35531     setRawValue : function(v){
35532         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35533     },
35534
35535     /**
35536      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35537      * @param {Mixed} value The value to set
35538      */
35539     setValue : function(v){
35540         this.value = v;
35541         if(this.rendered){
35542             this.el.dom.value = (v === null || v === undefined ? '' : v);
35543             this.validate();
35544         }
35545     },
35546
35547     adjustSize : function(w, h){
35548         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35549         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35550         return s;
35551     },
35552
35553     adjustWidth : function(tag, w){
35554         tag = tag.toLowerCase();
35555         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35556             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35557                 if(tag == 'input'){
35558                     return w + 2;
35559                 }
35560                 if(tag = 'textarea'){
35561                     return w-2;
35562                 }
35563             }else if(Roo.isOpera){
35564                 if(tag == 'input'){
35565                     return w + 2;
35566                 }
35567                 if(tag = 'textarea'){
35568                     return w-2;
35569                 }
35570             }
35571         }
35572         return w;
35573     }
35574 });
35575
35576
35577 // anything other than normal should be considered experimental
35578 Roo.form.Field.msgFx = {
35579     normal : {
35580         show: function(msgEl, f){
35581             msgEl.setDisplayed('block');
35582         },
35583
35584         hide : function(msgEl, f){
35585             msgEl.setDisplayed(false).update('');
35586         }
35587     },
35588
35589     slide : {
35590         show: function(msgEl, f){
35591             msgEl.slideIn('t', {stopFx:true});
35592         },
35593
35594         hide : function(msgEl, f){
35595             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35596         }
35597     },
35598
35599     slideRight : {
35600         show: function(msgEl, f){
35601             msgEl.fixDisplay();
35602             msgEl.alignTo(f.el, 'tl-tr');
35603             msgEl.slideIn('l', {stopFx:true});
35604         },
35605
35606         hide : function(msgEl, f){
35607             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35608         }
35609     }
35610 };/*
35611  * Based on:
35612  * Ext JS Library 1.1.1
35613  * Copyright(c) 2006-2007, Ext JS, LLC.
35614  *
35615  * Originally Released Under LGPL - original licence link has changed is not relivant.
35616  *
35617  * Fork - LGPL
35618  * <script type="text/javascript">
35619  */
35620  
35621
35622 /**
35623  * @class Roo.form.TextField
35624  * @extends Roo.form.Field
35625  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35626  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35627  * @constructor
35628  * Creates a new TextField
35629  * @param {Object} config Configuration options
35630  */
35631 Roo.form.TextField = function(config){
35632     Roo.form.TextField.superclass.constructor.call(this, config);
35633     this.addEvents({
35634         /**
35635          * @event autosize
35636          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35637          * according to the default logic, but this event provides a hook for the developer to apply additional
35638          * logic at runtime to resize the field if needed.
35639              * @param {Roo.form.Field} this This text field
35640              * @param {Number} width The new field width
35641              */
35642         autosize : true
35643     });
35644 };
35645
35646 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35647     /**
35648      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35649      */
35650     grow : false,
35651     /**
35652      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35653      */
35654     growMin : 30,
35655     /**
35656      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35657      */
35658     growMax : 800,
35659     /**
35660      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35661      */
35662     vtype : null,
35663     /**
35664      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35665      */
35666     maskRe : null,
35667     /**
35668      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35669      */
35670     disableKeyFilter : false,
35671     /**
35672      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35673      */
35674     allowBlank : true,
35675     /**
35676      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35677      */
35678     minLength : 0,
35679     /**
35680      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35681      */
35682     maxLength : Number.MAX_VALUE,
35683     /**
35684      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35685      */
35686     minLengthText : "The minimum length for this field is {0}",
35687     /**
35688      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35689      */
35690     maxLengthText : "The maximum length for this field is {0}",
35691     /**
35692      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35693      */
35694     selectOnFocus : false,
35695     /**
35696      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35697      */
35698     blankText : "This field is required",
35699     /**
35700      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35701      * If available, this function will be called only after the basic validators all return true, and will be passed the
35702      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35703      */
35704     validator : null,
35705     /**
35706      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35707      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35708      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35709      */
35710     regex : null,
35711     /**
35712      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35713      */
35714     regexText : "",
35715     /**
35716      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35717      */
35718     emptyText : null,
35719     /**
35720      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35721      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35722      */
35723     emptyClass : 'x-form-empty-field',
35724
35725     // private
35726     initEvents : function(){
35727         Roo.form.TextField.superclass.initEvents.call(this);
35728         if(this.validationEvent == 'keyup'){
35729             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35730             this.el.on('keyup', this.filterValidation, this);
35731         }
35732         else if(this.validationEvent !== false){
35733             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35734         }
35735         if(this.selectOnFocus || this.emptyText){
35736             this.on("focus", this.preFocus, this);
35737             if(this.emptyText){
35738                 this.on('blur', this.postBlur, this);
35739                 this.applyEmptyText();
35740             }
35741         }
35742         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35743             this.el.on("keypress", this.filterKeys, this);
35744         }
35745         if(this.grow){
35746             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35747             this.el.on("click", this.autoSize,  this);
35748         }
35749     },
35750
35751     processValue : function(value){
35752         if(this.stripCharsRe){
35753             var newValue = value.replace(this.stripCharsRe, '');
35754             if(newValue !== value){
35755                 this.setRawValue(newValue);
35756                 return newValue;
35757             }
35758         }
35759         return value;
35760     },
35761
35762     filterValidation : function(e){
35763         if(!e.isNavKeyPress()){
35764             this.validationTask.delay(this.validationDelay);
35765         }
35766     },
35767
35768     // private
35769     onKeyUp : function(e){
35770         if(!e.isNavKeyPress()){
35771             this.autoSize();
35772         }
35773     },
35774
35775     /**
35776      * Resets the current field value to the originally-loaded value and clears any validation messages.
35777      * Also adds emptyText and emptyClass if the original value was blank.
35778      */
35779     reset : function(){
35780         Roo.form.TextField.superclass.reset.call(this);
35781         this.applyEmptyText();
35782     },
35783
35784     applyEmptyText : function(){
35785         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35786             this.setRawValue(this.emptyText);
35787             this.el.addClass(this.emptyClass);
35788         }
35789     },
35790
35791     // private
35792     preFocus : function(){
35793         if(this.emptyText){
35794             if(this.el.dom.value == this.emptyText){
35795                 this.setRawValue('');
35796             }
35797             this.el.removeClass(this.emptyClass);
35798         }
35799         if(this.selectOnFocus){
35800             this.el.dom.select();
35801         }
35802     },
35803
35804     // private
35805     postBlur : function(){
35806         this.applyEmptyText();
35807     },
35808
35809     // private
35810     filterKeys : function(e){
35811         var k = e.getKey();
35812         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35813             return;
35814         }
35815         var c = e.getCharCode(), cc = String.fromCharCode(c);
35816         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35817             return;
35818         }
35819         if(!this.maskRe.test(cc)){
35820             e.stopEvent();
35821         }
35822     },
35823
35824     setValue : function(v){
35825         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35826             this.el.removeClass(this.emptyClass);
35827         }
35828         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35829         this.applyEmptyText();
35830         this.autoSize();
35831     },
35832
35833     /**
35834      * Validates a value according to the field's validation rules and marks the field as invalid
35835      * if the validation fails
35836      * @param {Mixed} value The value to validate
35837      * @return {Boolean} True if the value is valid, else false
35838      */
35839     validateValue : function(value){
35840         if(value.length < 1 || value === this.emptyText){ // if it's blank
35841              if(this.allowBlank){
35842                 this.clearInvalid();
35843                 return true;
35844              }else{
35845                 this.markInvalid(this.blankText);
35846                 return false;
35847              }
35848         }
35849         if(value.length < this.minLength){
35850             this.markInvalid(String.format(this.minLengthText, this.minLength));
35851             return false;
35852         }
35853         if(value.length > this.maxLength){
35854             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35855             return false;
35856         }
35857         if(this.vtype){
35858             var vt = Roo.form.VTypes;
35859             if(!vt[this.vtype](value, this)){
35860                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35861                 return false;
35862             }
35863         }
35864         if(typeof this.validator == "function"){
35865             var msg = this.validator(value);
35866             if(msg !== true){
35867                 this.markInvalid(msg);
35868                 return false;
35869             }
35870         }
35871         if(this.regex && !this.regex.test(value)){
35872             this.markInvalid(this.regexText);
35873             return false;
35874         }
35875         return true;
35876     },
35877
35878     /**
35879      * Selects text in this field
35880      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35881      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35882      */
35883     selectText : function(start, end){
35884         var v = this.getRawValue();
35885         if(v.length > 0){
35886             start = start === undefined ? 0 : start;
35887             end = end === undefined ? v.length : end;
35888             var d = this.el.dom;
35889             if(d.setSelectionRange){
35890                 d.setSelectionRange(start, end);
35891             }else if(d.createTextRange){
35892                 var range = d.createTextRange();
35893                 range.moveStart("character", start);
35894                 range.moveEnd("character", v.length-end);
35895                 range.select();
35896             }
35897         }
35898     },
35899
35900     /**
35901      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35902      * This only takes effect if grow = true, and fires the autosize event.
35903      */
35904     autoSize : function(){
35905         if(!this.grow || !this.rendered){
35906             return;
35907         }
35908         if(!this.metrics){
35909             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35910         }
35911         var el = this.el;
35912         var v = el.dom.value;
35913         var d = document.createElement('div');
35914         d.appendChild(document.createTextNode(v));
35915         v = d.innerHTML;
35916         d = null;
35917         v += "&#160;";
35918         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35919         this.el.setWidth(w);
35920         this.fireEvent("autosize", this, w);
35921     }
35922 });/*
35923  * Based on:
35924  * Ext JS Library 1.1.1
35925  * Copyright(c) 2006-2007, Ext JS, LLC.
35926  *
35927  * Originally Released Under LGPL - original licence link has changed is not relivant.
35928  *
35929  * Fork - LGPL
35930  * <script type="text/javascript">
35931  */
35932  
35933 /**
35934  * @class Roo.form.Hidden
35935  * @extends Roo.form.TextField
35936  * Simple Hidden element used on forms 
35937  * 
35938  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35939  * 
35940  * @constructor
35941  * Creates a new Hidden form element.
35942  * @param {Object} config Configuration options
35943  */
35944
35945
35946
35947 // easy hidden field...
35948 Roo.form.Hidden = function(config){
35949     Roo.form.Hidden.superclass.constructor.call(this, config);
35950 };
35951   
35952 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35953     fieldLabel:      '',
35954     inputType:      'hidden',
35955     width:          50,
35956     allowBlank:     true,
35957     labelSeparator: '',
35958     hidden:         true,
35959     itemCls :       'x-form-item-display-none'
35960
35961
35962 });
35963
35964
35965 /*
35966  * Based on:
35967  * Ext JS Library 1.1.1
35968  * Copyright(c) 2006-2007, Ext JS, LLC.
35969  *
35970  * Originally Released Under LGPL - original licence link has changed is not relivant.
35971  *
35972  * Fork - LGPL
35973  * <script type="text/javascript">
35974  */
35975  
35976 /**
35977  * @class Roo.form.TriggerField
35978  * @extends Roo.form.TextField
35979  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35980  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35981  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35982  * for which you can provide a custom implementation.  For example:
35983  * <pre><code>
35984 var trigger = new Roo.form.TriggerField();
35985 trigger.onTriggerClick = myTriggerFn;
35986 trigger.applyTo('my-field');
35987 </code></pre>
35988  *
35989  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35990  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35991  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35992  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35993  * @constructor
35994  * Create a new TriggerField.
35995  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35996  * to the base TextField)
35997  */
35998 Roo.form.TriggerField = function(config){
35999     this.mimicing = false;
36000     Roo.form.TriggerField.superclass.constructor.call(this, config);
36001 };
36002
36003 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36004     /**
36005      * @cfg {String} triggerClass A CSS class to apply to the trigger
36006      */
36007     /**
36008      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36009      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36010      */
36011     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36012     /**
36013      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36014      */
36015     hideTrigger:false,
36016
36017     /** @cfg {Boolean} grow @hide */
36018     /** @cfg {Number} growMin @hide */
36019     /** @cfg {Number} growMax @hide */
36020
36021     /**
36022      * @hide 
36023      * @method
36024      */
36025     autoSize: Roo.emptyFn,
36026     // private
36027     monitorTab : true,
36028     // private
36029     deferHeight : true,
36030
36031     
36032     actionMode : 'wrap',
36033     // private
36034     onResize : function(w, h){
36035         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36036         if(typeof w == 'number'){
36037             var x = w - this.trigger.getWidth();
36038             this.el.setWidth(this.adjustWidth('input', x));
36039             this.trigger.setStyle('left', x+'px');
36040         }
36041     },
36042
36043     // private
36044     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36045
36046     // private
36047     getResizeEl : function(){
36048         return this.wrap;
36049     },
36050
36051     // private
36052     getPositionEl : function(){
36053         return this.wrap;
36054     },
36055
36056     // private
36057     alignErrorIcon : function(){
36058         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36059     },
36060
36061     // private
36062     onRender : function(ct, position){
36063         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36064         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36065         this.trigger = this.wrap.createChild(this.triggerConfig ||
36066                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36067         if(this.hideTrigger){
36068             this.trigger.setDisplayed(false);
36069         }
36070         this.initTrigger();
36071         if(!this.width){
36072             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36073         }
36074     },
36075
36076     // private
36077     initTrigger : function(){
36078         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36079         this.trigger.addClassOnOver('x-form-trigger-over');
36080         this.trigger.addClassOnClick('x-form-trigger-click');
36081     },
36082
36083     // private
36084     onDestroy : function(){
36085         if(this.trigger){
36086             this.trigger.removeAllListeners();
36087             this.trigger.remove();
36088         }
36089         if(this.wrap){
36090             this.wrap.remove();
36091         }
36092         Roo.form.TriggerField.superclass.onDestroy.call(this);
36093     },
36094
36095     // private
36096     onFocus : function(){
36097         Roo.form.TriggerField.superclass.onFocus.call(this);
36098         if(!this.mimicing){
36099             this.wrap.addClass('x-trigger-wrap-focus');
36100             this.mimicing = true;
36101             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36102             if(this.monitorTab){
36103                 this.el.on("keydown", this.checkTab, this);
36104             }
36105         }
36106     },
36107
36108     // private
36109     checkTab : function(e){
36110         if(e.getKey() == e.TAB){
36111             this.triggerBlur();
36112         }
36113     },
36114
36115     // private
36116     onBlur : function(){
36117         // do nothing
36118     },
36119
36120     // private
36121     mimicBlur : function(e, t){
36122         if(!this.wrap.contains(t) && this.validateBlur()){
36123             this.triggerBlur();
36124         }
36125     },
36126
36127     // private
36128     triggerBlur : function(){
36129         this.mimicing = false;
36130         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36131         if(this.monitorTab){
36132             this.el.un("keydown", this.checkTab, this);
36133         }
36134         this.wrap.removeClass('x-trigger-wrap-focus');
36135         Roo.form.TriggerField.superclass.onBlur.call(this);
36136     },
36137
36138     // private
36139     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36140     validateBlur : function(e, t){
36141         return true;
36142     },
36143
36144     // private
36145     onDisable : function(){
36146         Roo.form.TriggerField.superclass.onDisable.call(this);
36147         if(this.wrap){
36148             this.wrap.addClass('x-item-disabled');
36149         }
36150     },
36151
36152     // private
36153     onEnable : function(){
36154         Roo.form.TriggerField.superclass.onEnable.call(this);
36155         if(this.wrap){
36156             this.wrap.removeClass('x-item-disabled');
36157         }
36158     },
36159
36160     // private
36161     onShow : function(){
36162         var ae = this.getActionEl();
36163         
36164         if(ae){
36165             ae.dom.style.display = '';
36166             ae.dom.style.visibility = 'visible';
36167         }
36168     },
36169
36170     // private
36171     
36172     onHide : function(){
36173         var ae = this.getActionEl();
36174         ae.dom.style.display = 'none';
36175     },
36176
36177     /**
36178      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36179      * by an implementing function.
36180      * @method
36181      * @param {EventObject} e
36182      */
36183     onTriggerClick : Roo.emptyFn
36184 });
36185
36186 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36187 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36188 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36189 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36190     initComponent : function(){
36191         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36192
36193         this.triggerConfig = {
36194             tag:'span', cls:'x-form-twin-triggers', cn:[
36195             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36196             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36197         ]};
36198     },
36199
36200     getTrigger : function(index){
36201         return this.triggers[index];
36202     },
36203
36204     initTrigger : function(){
36205         var ts = this.trigger.select('.x-form-trigger', true);
36206         this.wrap.setStyle('overflow', 'hidden');
36207         var triggerField = this;
36208         ts.each(function(t, all, index){
36209             t.hide = function(){
36210                 var w = triggerField.wrap.getWidth();
36211                 this.dom.style.display = 'none';
36212                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36213             };
36214             t.show = function(){
36215                 var w = triggerField.wrap.getWidth();
36216                 this.dom.style.display = '';
36217                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36218             };
36219             var triggerIndex = 'Trigger'+(index+1);
36220
36221             if(this['hide'+triggerIndex]){
36222                 t.dom.style.display = 'none';
36223             }
36224             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36225             t.addClassOnOver('x-form-trigger-over');
36226             t.addClassOnClick('x-form-trigger-click');
36227         }, this);
36228         this.triggers = ts.elements;
36229     },
36230
36231     onTrigger1Click : Roo.emptyFn,
36232     onTrigger2Click : Roo.emptyFn
36233 });/*
36234  * Based on:
36235  * Ext JS Library 1.1.1
36236  * Copyright(c) 2006-2007, Ext JS, LLC.
36237  *
36238  * Originally Released Under LGPL - original licence link has changed is not relivant.
36239  *
36240  * Fork - LGPL
36241  * <script type="text/javascript">
36242  */
36243  
36244 /**
36245  * @class Roo.form.TextArea
36246  * @extends Roo.form.TextField
36247  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36248  * support for auto-sizing.
36249  * @constructor
36250  * Creates a new TextArea
36251  * @param {Object} config Configuration options
36252  */
36253 Roo.form.TextArea = function(config){
36254     Roo.form.TextArea.superclass.constructor.call(this, config);
36255     // these are provided exchanges for backwards compat
36256     // minHeight/maxHeight were replaced by growMin/growMax to be
36257     // compatible with TextField growing config values
36258     if(this.minHeight !== undefined){
36259         this.growMin = this.minHeight;
36260     }
36261     if(this.maxHeight !== undefined){
36262         this.growMax = this.maxHeight;
36263     }
36264 };
36265
36266 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36267     /**
36268      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36269      */
36270     growMin : 60,
36271     /**
36272      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36273      */
36274     growMax: 1000,
36275     /**
36276      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36277      * in the field (equivalent to setting overflow: hidden, defaults to false)
36278      */
36279     preventScrollbars: false,
36280     /**
36281      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36282      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36283      */
36284
36285     // private
36286     onRender : function(ct, position){
36287         if(!this.el){
36288             this.defaultAutoCreate = {
36289                 tag: "textarea",
36290                 style:"width:300px;height:60px;",
36291                 autocomplete: "off"
36292             };
36293         }
36294         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36295         if(this.grow){
36296             this.textSizeEl = Roo.DomHelper.append(document.body, {
36297                 tag: "pre", cls: "x-form-grow-sizer"
36298             });
36299             if(this.preventScrollbars){
36300                 this.el.setStyle("overflow", "hidden");
36301             }
36302             this.el.setHeight(this.growMin);
36303         }
36304     },
36305
36306     onDestroy : function(){
36307         if(this.textSizeEl){
36308             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36309         }
36310         Roo.form.TextArea.superclass.onDestroy.call(this);
36311     },
36312
36313     // private
36314     onKeyUp : function(e){
36315         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36316             this.autoSize();
36317         }
36318     },
36319
36320     /**
36321      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36322      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36323      */
36324     autoSize : function(){
36325         if(!this.grow || !this.textSizeEl){
36326             return;
36327         }
36328         var el = this.el;
36329         var v = el.dom.value;
36330         var ts = this.textSizeEl;
36331
36332         ts.innerHTML = '';
36333         ts.appendChild(document.createTextNode(v));
36334         v = ts.innerHTML;
36335
36336         Roo.fly(ts).setWidth(this.el.getWidth());
36337         if(v.length < 1){
36338             v = "&#160;&#160;";
36339         }else{
36340             if(Roo.isIE){
36341                 v = v.replace(/\n/g, '<p>&#160;</p>');
36342             }
36343             v += "&#160;\n&#160;";
36344         }
36345         ts.innerHTML = v;
36346         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36347         if(h != this.lastHeight){
36348             this.lastHeight = h;
36349             this.el.setHeight(h);
36350             this.fireEvent("autosize", this, h);
36351         }
36352     }
36353 });/*
36354  * Based on:
36355  * Ext JS Library 1.1.1
36356  * Copyright(c) 2006-2007, Ext JS, LLC.
36357  *
36358  * Originally Released Under LGPL - original licence link has changed is not relivant.
36359  *
36360  * Fork - LGPL
36361  * <script type="text/javascript">
36362  */
36363  
36364
36365 /**
36366  * @class Roo.form.NumberField
36367  * @extends Roo.form.TextField
36368  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36369  * @constructor
36370  * Creates a new NumberField
36371  * @param {Object} config Configuration options
36372  */
36373 Roo.form.NumberField = function(config){
36374     Roo.form.NumberField.superclass.constructor.call(this, config);
36375 };
36376
36377 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36378     /**
36379      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36380      */
36381     fieldClass: "x-form-field x-form-num-field",
36382     /**
36383      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36384      */
36385     allowDecimals : true,
36386     /**
36387      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36388      */
36389     decimalSeparator : ".",
36390     /**
36391      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36392      */
36393     decimalPrecision : 2,
36394     /**
36395      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36396      */
36397     allowNegative : true,
36398     /**
36399      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36400      */
36401     minValue : Number.NEGATIVE_INFINITY,
36402     /**
36403      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36404      */
36405     maxValue : Number.MAX_VALUE,
36406     /**
36407      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36408      */
36409     minText : "The minimum value for this field is {0}",
36410     /**
36411      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36412      */
36413     maxText : "The maximum value for this field is {0}",
36414     /**
36415      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36416      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36417      */
36418     nanText : "{0} is not a valid number",
36419
36420     // private
36421     initEvents : function(){
36422         Roo.form.NumberField.superclass.initEvents.call(this);
36423         var allowed = "0123456789";
36424         if(this.allowDecimals){
36425             allowed += this.decimalSeparator;
36426         }
36427         if(this.allowNegative){
36428             allowed += "-";
36429         }
36430         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36431         var keyPress = function(e){
36432             var k = e.getKey();
36433             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36434                 return;
36435             }
36436             var c = e.getCharCode();
36437             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36438                 e.stopEvent();
36439             }
36440         };
36441         this.el.on("keypress", keyPress, this);
36442     },
36443
36444     // private
36445     validateValue : function(value){
36446         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36447             return false;
36448         }
36449         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36450              return true;
36451         }
36452         var num = this.parseValue(value);
36453         if(isNaN(num)){
36454             this.markInvalid(String.format(this.nanText, value));
36455             return false;
36456         }
36457         if(num < this.minValue){
36458             this.markInvalid(String.format(this.minText, this.minValue));
36459             return false;
36460         }
36461         if(num > this.maxValue){
36462             this.markInvalid(String.format(this.maxText, this.maxValue));
36463             return false;
36464         }
36465         return true;
36466     },
36467
36468     getValue : function(){
36469         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36470     },
36471
36472     // private
36473     parseValue : function(value){
36474         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36475         return isNaN(value) ? '' : value;
36476     },
36477
36478     // private
36479     fixPrecision : function(value){
36480         var nan = isNaN(value);
36481         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36482             return nan ? '' : value;
36483         }
36484         return parseFloat(value).toFixed(this.decimalPrecision);
36485     },
36486
36487     setValue : function(v){
36488         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36489     },
36490
36491     // private
36492     decimalPrecisionFcn : function(v){
36493         return Math.floor(v);
36494     },
36495
36496     beforeBlur : function(){
36497         var v = this.parseValue(this.getRawValue());
36498         if(v){
36499             this.setValue(this.fixPrecision(v));
36500         }
36501     }
36502 });/*
36503  * Based on:
36504  * Ext JS Library 1.1.1
36505  * Copyright(c) 2006-2007, Ext JS, LLC.
36506  *
36507  * Originally Released Under LGPL - original licence link has changed is not relivant.
36508  *
36509  * Fork - LGPL
36510  * <script type="text/javascript">
36511  */
36512  
36513 /**
36514  * @class Roo.form.DateField
36515  * @extends Roo.form.TriggerField
36516  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36517 * @constructor
36518 * Create a new DateField
36519 * @param {Object} config
36520  */
36521 Roo.form.DateField = function(config){
36522     Roo.form.DateField.superclass.constructor.call(this, config);
36523     
36524       this.addEvents({
36525          
36526         /**
36527          * @event select
36528          * Fires when a date is selected
36529              * @param {Roo.form.DateField} combo This combo box
36530              * @param {Date} date The date selected
36531              */
36532         'select' : true
36533          
36534     });
36535     
36536     
36537     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36538     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36539     this.ddMatch = null;
36540     if(this.disabledDates){
36541         var dd = this.disabledDates;
36542         var re = "(?:";
36543         for(var i = 0; i < dd.length; i++){
36544             re += dd[i];
36545             if(i != dd.length-1) re += "|";
36546         }
36547         this.ddMatch = new RegExp(re + ")");
36548     }
36549 };
36550
36551 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36552     /**
36553      * @cfg {String} format
36554      * The default date format string which can be overriden for localization support.  The format must be
36555      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36556      */
36557     format : "m/d/y",
36558     /**
36559      * @cfg {String} altFormats
36560      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36561      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36562      */
36563     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36564     /**
36565      * @cfg {Array} disabledDays
36566      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36567      */
36568     disabledDays : null,
36569     /**
36570      * @cfg {String} disabledDaysText
36571      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36572      */
36573     disabledDaysText : "Disabled",
36574     /**
36575      * @cfg {Array} disabledDates
36576      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36577      * expression so they are very powerful. Some examples:
36578      * <ul>
36579      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36580      * <li>["03/08", "09/16"] would disable those days for every year</li>
36581      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36582      * <li>["03/../2006"] would disable every day in March 2006</li>
36583      * <li>["^03"] would disable every day in every March</li>
36584      * </ul>
36585      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36586      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36587      */
36588     disabledDates : null,
36589     /**
36590      * @cfg {String} disabledDatesText
36591      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36592      */
36593     disabledDatesText : "Disabled",
36594     /**
36595      * @cfg {Date/String} minValue
36596      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36597      * valid format (defaults to null).
36598      */
36599     minValue : null,
36600     /**
36601      * @cfg {Date/String} maxValue
36602      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36603      * valid format (defaults to null).
36604      */
36605     maxValue : null,
36606     /**
36607      * @cfg {String} minText
36608      * The error text to display when the date in the cell is before minValue (defaults to
36609      * 'The date in this field must be after {minValue}').
36610      */
36611     minText : "The date in this field must be equal to or after {0}",
36612     /**
36613      * @cfg {String} maxText
36614      * The error text to display when the date in the cell is after maxValue (defaults to
36615      * 'The date in this field must be before {maxValue}').
36616      */
36617     maxText : "The date in this field must be equal to or before {0}",
36618     /**
36619      * @cfg {String} invalidText
36620      * The error text to display when the date in the field is invalid (defaults to
36621      * '{value} is not a valid date - it must be in the format {format}').
36622      */
36623     invalidText : "{0} is not a valid date - it must be in the format {1}",
36624     /**
36625      * @cfg {String} triggerClass
36626      * An additional CSS class used to style the trigger button.  The trigger will always get the
36627      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36628      * which displays a calendar icon).
36629      */
36630     triggerClass : 'x-form-date-trigger',
36631     
36632
36633     /**
36634      * @cfg {bool} useIso
36635      * if enabled, then the date field will use a hidden field to store the 
36636      * real value as iso formated date. default (false)
36637      */ 
36638     useIso : false,
36639     /**
36640      * @cfg {String/Object} autoCreate
36641      * A DomHelper element spec, or true for a default element spec (defaults to
36642      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36643      */ 
36644     // private
36645     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36646     
36647     // private
36648     hiddenField: false,
36649     
36650     onRender : function(ct, position)
36651     {
36652         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36653         if (this.useIso) {
36654             this.el.dom.removeAttribute('name'); 
36655             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36656                     'before', true);
36657             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36658             // prevent input submission
36659             this.hiddenName = this.name;
36660         }
36661             
36662             
36663     },
36664     
36665     // private
36666     validateValue : function(value)
36667     {
36668         value = this.formatDate(value);
36669         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36670             return false;
36671         }
36672         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36673              return true;
36674         }
36675         var svalue = value;
36676         value = this.parseDate(value);
36677         if(!value){
36678             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36679             return false;
36680         }
36681         var time = value.getTime();
36682         if(this.minValue && time < this.minValue.getTime()){
36683             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36684             return false;
36685         }
36686         if(this.maxValue && time > this.maxValue.getTime()){
36687             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36688             return false;
36689         }
36690         if(this.disabledDays){
36691             var day = value.getDay();
36692             for(var i = 0; i < this.disabledDays.length; i++) {
36693                 if(day === this.disabledDays[i]){
36694                     this.markInvalid(this.disabledDaysText);
36695                     return false;
36696                 }
36697             }
36698         }
36699         var fvalue = this.formatDate(value);
36700         if(this.ddMatch && this.ddMatch.test(fvalue)){
36701             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36702             return false;
36703         }
36704         return true;
36705     },
36706
36707     // private
36708     // Provides logic to override the default TriggerField.validateBlur which just returns true
36709     validateBlur : function(){
36710         return !this.menu || !this.menu.isVisible();
36711     },
36712
36713     /**
36714      * Returns the current date value of the date field.
36715      * @return {Date} The date value
36716      */
36717     getValue : function(){
36718         
36719         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36720     },
36721
36722     /**
36723      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36724      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36725      * (the default format used is "m/d/y").
36726      * <br />Usage:
36727      * <pre><code>
36728 //All of these calls set the same date value (May 4, 2006)
36729
36730 //Pass a date object:
36731 var dt = new Date('5/4/06');
36732 dateField.setValue(dt);
36733
36734 //Pass a date string (default format):
36735 dateField.setValue('5/4/06');
36736
36737 //Pass a date string (custom format):
36738 dateField.format = 'Y-m-d';
36739 dateField.setValue('2006-5-4');
36740 </code></pre>
36741      * @param {String/Date} date The date or valid date string
36742      */
36743     setValue : function(date){
36744         if (this.hiddenField) {
36745             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36746         }
36747         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36748     },
36749
36750     // private
36751     parseDate : function(value){
36752         if(!value || value instanceof Date){
36753             return value;
36754         }
36755         var v = Date.parseDate(value, this.format);
36756         if(!v && this.altFormats){
36757             if(!this.altFormatsArray){
36758                 this.altFormatsArray = this.altFormats.split("|");
36759             }
36760             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36761                 v = Date.parseDate(value, this.altFormatsArray[i]);
36762             }
36763         }
36764         return v;
36765     },
36766
36767     // private
36768     formatDate : function(date, fmt){
36769         return (!date || !(date instanceof Date)) ?
36770                date : date.dateFormat(fmt || this.format);
36771     },
36772
36773     // private
36774     menuListeners : {
36775         select: function(m, d){
36776             this.setValue(d);
36777             this.fireEvent('select', this, d);
36778         },
36779         show : function(){ // retain focus styling
36780             this.onFocus();
36781         },
36782         hide : function(){
36783             this.focus.defer(10, this);
36784             var ml = this.menuListeners;
36785             this.menu.un("select", ml.select,  this);
36786             this.menu.un("show", ml.show,  this);
36787             this.menu.un("hide", ml.hide,  this);
36788         }
36789     },
36790
36791     // private
36792     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36793     onTriggerClick : function(){
36794         if(this.disabled){
36795             return;
36796         }
36797         if(this.menu == null){
36798             this.menu = new Roo.menu.DateMenu();
36799         }
36800         Roo.apply(this.menu.picker,  {
36801             showClear: this.allowBlank,
36802             minDate : this.minValue,
36803             maxDate : this.maxValue,
36804             disabledDatesRE : this.ddMatch,
36805             disabledDatesText : this.disabledDatesText,
36806             disabledDays : this.disabledDays,
36807             disabledDaysText : this.disabledDaysText,
36808             format : this.format,
36809             minText : String.format(this.minText, this.formatDate(this.minValue)),
36810             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36811         });
36812         this.menu.on(Roo.apply({}, this.menuListeners, {
36813             scope:this
36814         }));
36815         this.menu.picker.setValue(this.getValue() || new Date());
36816         this.menu.show(this.el, "tl-bl?");
36817     },
36818
36819     beforeBlur : function(){
36820         var v = this.parseDate(this.getRawValue());
36821         if(v){
36822             this.setValue(v);
36823         }
36824     }
36825
36826     /** @cfg {Boolean} grow @hide */
36827     /** @cfg {Number} growMin @hide */
36828     /** @cfg {Number} growMax @hide */
36829     /**
36830      * @hide
36831      * @method autoSize
36832      */
36833 });/*
36834  * Based on:
36835  * Ext JS Library 1.1.1
36836  * Copyright(c) 2006-2007, Ext JS, LLC.
36837  *
36838  * Originally Released Under LGPL - original licence link has changed is not relivant.
36839  *
36840  * Fork - LGPL
36841  * <script type="text/javascript">
36842  */
36843  
36844
36845 /**
36846  * @class Roo.form.ComboBox
36847  * @extends Roo.form.TriggerField
36848  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36849  * @constructor
36850  * Create a new ComboBox.
36851  * @param {Object} config Configuration options
36852  */
36853 Roo.form.ComboBox = function(config){
36854     Roo.form.ComboBox.superclass.constructor.call(this, config);
36855     this.addEvents({
36856         /**
36857          * @event expand
36858          * Fires when the dropdown list is expanded
36859              * @param {Roo.form.ComboBox} combo This combo box
36860              */
36861         'expand' : true,
36862         /**
36863          * @event collapse
36864          * Fires when the dropdown list is collapsed
36865              * @param {Roo.form.ComboBox} combo This combo box
36866              */
36867         'collapse' : true,
36868         /**
36869          * @event beforeselect
36870          * Fires before a list item is selected. Return false to cancel the selection.
36871              * @param {Roo.form.ComboBox} combo This combo box
36872              * @param {Roo.data.Record} record The data record returned from the underlying store
36873              * @param {Number} index The index of the selected item in the dropdown list
36874              */
36875         'beforeselect' : true,
36876         /**
36877          * @event select
36878          * Fires when a list item is selected
36879              * @param {Roo.form.ComboBox} combo This combo box
36880              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36881              * @param {Number} index The index of the selected item in the dropdown list
36882              */
36883         'select' : true,
36884         /**
36885          * @event beforequery
36886          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36887          * The event object passed has these properties:
36888              * @param {Roo.form.ComboBox} combo This combo box
36889              * @param {String} query The query
36890              * @param {Boolean} forceAll true to force "all" query
36891              * @param {Boolean} cancel true to cancel the query
36892              * @param {Object} e The query event object
36893              */
36894         'beforequery': true,
36895          /**
36896          * @event add
36897          * Fires when the 'add' icon is pressed (add a listener to enable add button)
36898              * @param {Roo.form.ComboBox} combo This combo box
36899              */
36900         'add' : true,
36901         /**
36902          * @event edit
36903          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
36904              * @param {Roo.form.ComboBox} combo This combo box
36905              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
36906              */
36907         'edit' : true
36908         
36909         
36910     });
36911     if(this.transform){
36912         this.allowDomMove = false;
36913         var s = Roo.getDom(this.transform);
36914         if(!this.hiddenName){
36915             this.hiddenName = s.name;
36916         }
36917         if(!this.store){
36918             this.mode = 'local';
36919             var d = [], opts = s.options;
36920             for(var i = 0, len = opts.length;i < len; i++){
36921                 var o = opts[i];
36922                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36923                 if(o.selected) {
36924                     this.value = value;
36925                 }
36926                 d.push([value, o.text]);
36927             }
36928             this.store = new Roo.data.SimpleStore({
36929                 'id': 0,
36930                 fields: ['value', 'text'],
36931                 data : d
36932             });
36933             this.valueField = 'value';
36934             this.displayField = 'text';
36935         }
36936         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36937         if(!this.lazyRender){
36938             this.target = true;
36939             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36940             s.parentNode.removeChild(s); // remove it
36941             this.render(this.el.parentNode);
36942         }else{
36943             s.parentNode.removeChild(s); // remove it
36944         }
36945
36946     }
36947     if (this.store) {
36948         this.store = Roo.factory(this.store, Roo.data);
36949     }
36950     
36951     this.selectedIndex = -1;
36952     if(this.mode == 'local'){
36953         if(config.queryDelay === undefined){
36954             this.queryDelay = 10;
36955         }
36956         if(config.minChars === undefined){
36957             this.minChars = 0;
36958         }
36959     }
36960 };
36961
36962 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36963     /**
36964      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36965      */
36966     /**
36967      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36968      * rendering into an Roo.Editor, defaults to false)
36969      */
36970     /**
36971      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36972      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36973      */
36974     /**
36975      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36976      */
36977     /**
36978      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36979      * the dropdown list (defaults to undefined, with no header element)
36980      */
36981
36982      /**
36983      * @cfg {String/Roo.Template} tpl The template to use to render the output
36984      */
36985      
36986     // private
36987     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36988     /**
36989      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36990      */
36991     listWidth: undefined,
36992     /**
36993      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36994      * mode = 'remote' or 'text' if mode = 'local')
36995      */
36996     displayField: undefined,
36997     /**
36998      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36999      * mode = 'remote' or 'value' if mode = 'local'). 
37000      * Note: use of a valueField requires the user make a selection
37001      * in order for a value to be mapped.
37002      */
37003     valueField: undefined,
37004     /**
37005      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37006      * field's data value (defaults to the underlying DOM element's name)
37007      */
37008     hiddenName: undefined,
37009     /**
37010      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37011      */
37012     listClass: '',
37013     /**
37014      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37015      */
37016     selectedClass: 'x-combo-selected',
37017     /**
37018      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37019      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37020      * which displays a downward arrow icon).
37021      */
37022     triggerClass : 'x-form-arrow-trigger',
37023     /**
37024      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37025      */
37026     shadow:'sides',
37027     /**
37028      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37029      * anchor positions (defaults to 'tl-bl')
37030      */
37031     listAlign: 'tl-bl?',
37032     /**
37033      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37034      */
37035     maxHeight: 300,
37036     /**
37037      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37038      * query specified by the allQuery config option (defaults to 'query')
37039      */
37040     triggerAction: 'query',
37041     /**
37042      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37043      * (defaults to 4, does not apply if editable = false)
37044      */
37045     minChars : 4,
37046     /**
37047      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37048      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37049      */
37050     typeAhead: false,
37051     /**
37052      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37053      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37054      */
37055     queryDelay: 500,
37056     /**
37057      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37058      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37059      */
37060     pageSize: 0,
37061     /**
37062      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37063      * when editable = true (defaults to false)
37064      */
37065     selectOnFocus:false,
37066     /**
37067      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37068      */
37069     queryParam: 'query',
37070     /**
37071      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37072      * when mode = 'remote' (defaults to 'Loading...')
37073      */
37074     loadingText: 'Loading...',
37075     /**
37076      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37077      */
37078     resizable: false,
37079     /**
37080      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37081      */
37082     handleHeight : 8,
37083     /**
37084      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37085      * traditional select (defaults to true)
37086      */
37087     editable: true,
37088     /**
37089      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37090      */
37091     allQuery: '',
37092     /**
37093      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37094      */
37095     mode: 'remote',
37096     /**
37097      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37098      * listWidth has a higher value)
37099      */
37100     minListWidth : 70,
37101     /**
37102      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37103      * allow the user to set arbitrary text into the field (defaults to false)
37104      */
37105     forceSelection:false,
37106     /**
37107      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37108      * if typeAhead = true (defaults to 250)
37109      */
37110     typeAheadDelay : 250,
37111     /**
37112      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37113      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37114      */
37115     valueNotFoundText : undefined,
37116     /**
37117      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37118      */
37119     blockFocus : false,
37120     
37121     /**
37122      * @cfg {bool} disableClear Disable showing of clear button.
37123      */
37124     disableClear : false,
37125     
37126     //private
37127     addicon : false,
37128     editicon: false,
37129     
37130     
37131     // private
37132     onRender : function(ct, position){
37133         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37134         if(this.hiddenName){
37135             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37136                     'before', true);
37137             this.hiddenField.value =
37138                 this.hiddenValue !== undefined ? this.hiddenValue :
37139                 this.value !== undefined ? this.value : '';
37140
37141             // prevent input submission
37142             this.el.dom.removeAttribute('name');
37143         }
37144         if(Roo.isGecko){
37145             this.el.dom.setAttribute('autocomplete', 'off');
37146         }
37147
37148         var cls = 'x-combo-list';
37149
37150         this.list = new Roo.Layer({
37151             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37152         });
37153
37154         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37155         this.list.setWidth(lw);
37156         this.list.swallowEvent('mousewheel');
37157         this.assetHeight = 0;
37158
37159         if(this.title){
37160             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37161             this.assetHeight += this.header.getHeight();
37162         }
37163
37164         this.innerList = this.list.createChild({cls:cls+'-inner'});
37165         this.innerList.on('mouseover', this.onViewOver, this);
37166         this.innerList.on('mousemove', this.onViewMove, this);
37167         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37168         
37169         if(this.allowBlank && !this.pageSize && !this.disableClear){
37170             this.footer = this.list.createChild({cls:cls+'-ft'});
37171             this.pageTb = new Roo.Toolbar(this.footer);
37172            
37173         }
37174         if(this.pageSize){
37175             this.footer = this.list.createChild({cls:cls+'-ft'});
37176             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37177                     {pageSize: this.pageSize});
37178             
37179         }
37180         
37181         if (this.pageTb && this.allowBlank && !this.disableClear) {
37182             var _this = this;
37183             this.pageTb.add(new Roo.Toolbar.Fill(), {
37184                 cls: 'x-btn-icon x-btn-clear',
37185                 text: '&#160;',
37186                 handler: function()
37187                 {
37188                     _this.collapse();
37189                     _this.clearValue();
37190                     _this.onSelect(false, -1);
37191                 }
37192             });
37193         }
37194         if (this.footer) {
37195             this.assetHeight += this.footer.getHeight();
37196         }
37197         
37198
37199         if(!this.tpl){
37200             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37201         }
37202
37203         this.view = new Roo.View(this.innerList, this.tpl, {
37204             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37205         });
37206
37207         this.view.on('click', this.onViewClick, this);
37208
37209         this.store.on('beforeload', this.onBeforeLoad, this);
37210         this.store.on('load', this.onLoad, this);
37211         this.store.on('loadexception', this.collapse, this);
37212
37213         if(this.resizable){
37214             this.resizer = new Roo.Resizable(this.list,  {
37215                pinned:true, handles:'se'
37216             });
37217             this.resizer.on('resize', function(r, w, h){
37218                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37219                 this.listWidth = w;
37220                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37221                 this.restrictHeight();
37222             }, this);
37223             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37224         }
37225         if(!this.editable){
37226             this.editable = true;
37227             this.setEditable(false);
37228         }  
37229         
37230         
37231         if (typeof(this.events.add.listeners) != 'undefined') {
37232             
37233             this.addicon = this.wrap.createChild(
37234                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37235        
37236             this.addicon.on('click', function(e) {
37237                 this.fireEvent('add', this);
37238             }, this);
37239         }
37240         if (typeof(this.events.edit.listeners) != 'undefined') {
37241             
37242             this.editicon = this.wrap.createChild(
37243                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37244             if (this.addicon) {
37245                 this.editicon.setStyle('margin-left', '40px');
37246             }
37247             this.editicon.on('click', function(e) {
37248                 
37249                 // we fire even  if inothing is selected..
37250                 this.fireEvent('edit', this, this.lastData );
37251                 
37252             }, this);
37253         }
37254         
37255         
37256         
37257     },
37258
37259     // private
37260     initEvents : function(){
37261         Roo.form.ComboBox.superclass.initEvents.call(this);
37262
37263         this.keyNav = new Roo.KeyNav(this.el, {
37264             "up" : function(e){
37265                 this.inKeyMode = true;
37266                 this.selectPrev();
37267             },
37268
37269             "down" : function(e){
37270                 if(!this.isExpanded()){
37271                     this.onTriggerClick();
37272                 }else{
37273                     this.inKeyMode = true;
37274                     this.selectNext();
37275                 }
37276             },
37277
37278             "enter" : function(e){
37279                 this.onViewClick();
37280                 //return true;
37281             },
37282
37283             "esc" : function(e){
37284                 this.collapse();
37285             },
37286
37287             "tab" : function(e){
37288                 this.onViewClick(false);
37289                 return true;
37290             },
37291
37292             scope : this,
37293
37294             doRelay : function(foo, bar, hname){
37295                 if(hname == 'down' || this.scope.isExpanded()){
37296                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37297                 }
37298                 return true;
37299             },
37300
37301             forceKeyDown: true
37302         });
37303         this.queryDelay = Math.max(this.queryDelay || 10,
37304                 this.mode == 'local' ? 10 : 250);
37305         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37306         if(this.typeAhead){
37307             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37308         }
37309         if(this.editable !== false){
37310             this.el.on("keyup", this.onKeyUp, this);
37311         }
37312         if(this.forceSelection){
37313             this.on('blur', this.doForce, this);
37314         }
37315     },
37316
37317     onDestroy : function(){
37318         if(this.view){
37319             this.view.setStore(null);
37320             this.view.el.removeAllListeners();
37321             this.view.el.remove();
37322             this.view.purgeListeners();
37323         }
37324         if(this.list){
37325             this.list.destroy();
37326         }
37327         if(this.store){
37328             this.store.un('beforeload', this.onBeforeLoad, this);
37329             this.store.un('load', this.onLoad, this);
37330             this.store.un('loadexception', this.collapse, this);
37331         }
37332         Roo.form.ComboBox.superclass.onDestroy.call(this);
37333     },
37334
37335     // private
37336     fireKey : function(e){
37337         if(e.isNavKeyPress() && !this.list.isVisible()){
37338             this.fireEvent("specialkey", this, e);
37339         }
37340     },
37341
37342     // private
37343     onResize: function(w, h){
37344         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37345         
37346         if(typeof w != 'number'){
37347             // we do not handle it!?!?
37348             return;
37349         }
37350         var tw = this.trigger.getWidth();
37351         tw += this.addicon ? this.addicon.getWidth() : 0;
37352         tw += this.editicon ? this.editicon.getWidth() : 0;
37353         var x = w - tw;
37354         this.el.setWidth( this.adjustWidth('input', x));
37355             
37356         this.trigger.setStyle('left', x+'px');
37357         
37358         if(this.list && this.listWidth === undefined){
37359             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37360             this.list.setWidth(lw);
37361             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37362         }
37363         
37364     
37365         
37366     },
37367
37368     /**
37369      * Allow or prevent the user from directly editing the field text.  If false is passed,
37370      * the user will only be able to select from the items defined in the dropdown list.  This method
37371      * is the runtime equivalent of setting the 'editable' config option at config time.
37372      * @param {Boolean} value True to allow the user to directly edit the field text
37373      */
37374     setEditable : function(value){
37375         if(value == this.editable){
37376             return;
37377         }
37378         this.editable = value;
37379         if(!value){
37380             this.el.dom.setAttribute('readOnly', true);
37381             this.el.on('mousedown', this.onTriggerClick,  this);
37382             this.el.addClass('x-combo-noedit');
37383         }else{
37384             this.el.dom.setAttribute('readOnly', false);
37385             this.el.un('mousedown', this.onTriggerClick,  this);
37386             this.el.removeClass('x-combo-noedit');
37387         }
37388     },
37389
37390     // private
37391     onBeforeLoad : function(){
37392         if(!this.hasFocus){
37393             return;
37394         }
37395         this.innerList.update(this.loadingText ?
37396                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37397         this.restrictHeight();
37398         this.selectedIndex = -1;
37399     },
37400
37401     // private
37402     onLoad : function(){
37403         if(!this.hasFocus){
37404             return;
37405         }
37406         if(this.store.getCount() > 0){
37407             this.expand();
37408             this.restrictHeight();
37409             if(this.lastQuery == this.allQuery){
37410                 if(this.editable){
37411                     this.el.dom.select();
37412                 }
37413                 if(!this.selectByValue(this.value, true)){
37414                     this.select(0, true);
37415                 }
37416             }else{
37417                 this.selectNext();
37418                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37419                     this.taTask.delay(this.typeAheadDelay);
37420                 }
37421             }
37422         }else{
37423             this.onEmptyResults();
37424         }
37425         //this.el.focus();
37426     },
37427
37428     // private
37429     onTypeAhead : function(){
37430         if(this.store.getCount() > 0){
37431             var r = this.store.getAt(0);
37432             var newValue = r.data[this.displayField];
37433             var len = newValue.length;
37434             var selStart = this.getRawValue().length;
37435             if(selStart != len){
37436                 this.setRawValue(newValue);
37437                 this.selectText(selStart, newValue.length);
37438             }
37439         }
37440     },
37441
37442     // private
37443     onSelect : function(record, index){
37444         if(this.fireEvent('beforeselect', this, record, index) !== false){
37445             this.setFromData(index > -1 ? record.data : false);
37446             this.collapse();
37447             this.fireEvent('select', this, record, index);
37448         }
37449     },
37450
37451     /**
37452      * Returns the currently selected field value or empty string if no value is set.
37453      * @return {String} value The selected value
37454      */
37455     getValue : function(){
37456         if(this.valueField){
37457             return typeof this.value != 'undefined' ? this.value : '';
37458         }else{
37459             return Roo.form.ComboBox.superclass.getValue.call(this);
37460         }
37461     },
37462
37463     /**
37464      * Clears any text/value currently set in the field
37465      */
37466     clearValue : function(){
37467         if(this.hiddenField){
37468             this.hiddenField.value = '';
37469         }
37470         this.value = '';
37471         this.setRawValue('');
37472         this.lastSelectionText = '';
37473         this.applyEmptyText();
37474     },
37475
37476     /**
37477      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37478      * will be displayed in the field.  If the value does not match the data value of an existing item,
37479      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37480      * Otherwise the field will be blank (although the value will still be set).
37481      * @param {String} value The value to match
37482      */
37483     setValue : function(v){
37484         var text = v;
37485         if(this.valueField){
37486             var r = this.findRecord(this.valueField, v);
37487             if(r){
37488                 text = r.data[this.displayField];
37489             }else if(this.valueNotFoundText !== undefined){
37490                 text = this.valueNotFoundText;
37491             }
37492         }
37493         this.lastSelectionText = text;
37494         if(this.hiddenField){
37495             this.hiddenField.value = v;
37496         }
37497         Roo.form.ComboBox.superclass.setValue.call(this, text);
37498         this.value = v;
37499     },
37500     /**
37501      * @property {Object} the last set data for the element
37502      */
37503     
37504     lastData : false,
37505     /**
37506      * Sets the value of the field based on a object which is related to the record format for the store.
37507      * @param {Object} value the value to set as. or false on reset?
37508      */
37509     setFromData : function(o){
37510         var dv = ''; // display value
37511         var vv = ''; // value value..
37512         this.lastData = o;
37513         if (this.displayField) {
37514             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37515         } else {
37516             // this is an error condition!!!
37517             console.log('no value field set for '+ this.name);
37518         }
37519         
37520         if(this.valueField){
37521             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37522         }
37523         if(this.hiddenField){
37524             this.hiddenField.value = vv;
37525             
37526             this.lastSelectionText = dv;
37527             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37528             this.value = vv;
37529             return;
37530         }
37531         // no hidden field.. - we store the value in 'value', but still display
37532         // display field!!!!
37533         this.lastSelectionText = dv;
37534         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37535         this.value = vv;
37536         
37537         
37538     },
37539     // private
37540     reset : function(){
37541         // overridden so that last data is reset..
37542         this.setValue(this.originalValue);
37543         this.clearInvalid();
37544         this.lastData = false;
37545     },
37546     // private
37547     findRecord : function(prop, value){
37548         var record;
37549         if(this.store.getCount() > 0){
37550             this.store.each(function(r){
37551                 if(r.data[prop] == value){
37552                     record = r;
37553                     return false;
37554                 }
37555             });
37556         }
37557         return record;
37558     },
37559
37560     // private
37561     onViewMove : function(e, t){
37562         this.inKeyMode = false;
37563     },
37564
37565     // private
37566     onViewOver : function(e, t){
37567         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37568             return;
37569         }
37570         var item = this.view.findItemFromChild(t);
37571         if(item){
37572             var index = this.view.indexOf(item);
37573             this.select(index, false);
37574         }
37575     },
37576
37577     // private
37578     onViewClick : function(doFocus){
37579         var index = this.view.getSelectedIndexes()[0];
37580         var r = this.store.getAt(index);
37581         if(r){
37582             this.onSelect(r, index);
37583         }
37584         if(doFocus !== false && !this.blockFocus){
37585             this.el.focus();
37586         }
37587     },
37588
37589     // private
37590     restrictHeight : function(){
37591         this.innerList.dom.style.height = '';
37592         var inner = this.innerList.dom;
37593         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37594         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37595         this.list.beginUpdate();
37596         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37597         this.list.alignTo(this.el, this.listAlign);
37598         this.list.endUpdate();
37599     },
37600
37601     // private
37602     onEmptyResults : function(){
37603         this.collapse();
37604     },
37605
37606     /**
37607      * Returns true if the dropdown list is expanded, else false.
37608      */
37609     isExpanded : function(){
37610         return this.list.isVisible();
37611     },
37612
37613     /**
37614      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37615      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37616      * @param {String} value The data value of the item to select
37617      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37618      * selected item if it is not currently in view (defaults to true)
37619      * @return {Boolean} True if the value matched an item in the list, else false
37620      */
37621     selectByValue : function(v, scrollIntoView){
37622         if(v !== undefined && v !== null){
37623             var r = this.findRecord(this.valueField || this.displayField, v);
37624             if(r){
37625                 this.select(this.store.indexOf(r), scrollIntoView);
37626                 return true;
37627             }
37628         }
37629         return false;
37630     },
37631
37632     /**
37633      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37634      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37635      * @param {Number} index The zero-based index of the list item to select
37636      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37637      * selected item if it is not currently in view (defaults to true)
37638      */
37639     select : function(index, scrollIntoView){
37640         this.selectedIndex = index;
37641         this.view.select(index);
37642         if(scrollIntoView !== false){
37643             var el = this.view.getNode(index);
37644             if(el){
37645                 this.innerList.scrollChildIntoView(el, false);
37646             }
37647         }
37648     },
37649
37650     // private
37651     selectNext : function(){
37652         var ct = this.store.getCount();
37653         if(ct > 0){
37654             if(this.selectedIndex == -1){
37655                 this.select(0);
37656             }else if(this.selectedIndex < ct-1){
37657                 this.select(this.selectedIndex+1);
37658             }
37659         }
37660     },
37661
37662     // private
37663     selectPrev : function(){
37664         var ct = this.store.getCount();
37665         if(ct > 0){
37666             if(this.selectedIndex == -1){
37667                 this.select(0);
37668             }else if(this.selectedIndex != 0){
37669                 this.select(this.selectedIndex-1);
37670             }
37671         }
37672     },
37673
37674     // private
37675     onKeyUp : function(e){
37676         if(this.editable !== false && !e.isSpecialKey()){
37677             this.lastKey = e.getKey();
37678             this.dqTask.delay(this.queryDelay);
37679         }
37680     },
37681
37682     // private
37683     validateBlur : function(){
37684         return !this.list || !this.list.isVisible();   
37685     },
37686
37687     // private
37688     initQuery : function(){
37689         this.doQuery(this.getRawValue());
37690     },
37691
37692     // private
37693     doForce : function(){
37694         if(this.el.dom.value.length > 0){
37695             this.el.dom.value =
37696                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37697             this.applyEmptyText();
37698         }
37699     },
37700
37701     /**
37702      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37703      * query allowing the query action to be canceled if needed.
37704      * @param {String} query The SQL query to execute
37705      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37706      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37707      * saved in the current store (defaults to false)
37708      */
37709     doQuery : function(q, forceAll){
37710         if(q === undefined || q === null){
37711             q = '';
37712         }
37713         var qe = {
37714             query: q,
37715             forceAll: forceAll,
37716             combo: this,
37717             cancel:false
37718         };
37719         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37720             return false;
37721         }
37722         q = qe.query;
37723         forceAll = qe.forceAll;
37724         if(forceAll === true || (q.length >= this.minChars)){
37725             if(this.lastQuery != q){
37726                 this.lastQuery = q;
37727                 if(this.mode == 'local'){
37728                     this.selectedIndex = -1;
37729                     if(forceAll){
37730                         this.store.clearFilter();
37731                     }else{
37732                         this.store.filter(this.displayField, q);
37733                     }
37734                     this.onLoad();
37735                 }else{
37736                     this.store.baseParams[this.queryParam] = q;
37737                     this.store.load({
37738                         params: this.getParams(q)
37739                     });
37740                     this.expand();
37741                 }
37742             }else{
37743                 this.selectedIndex = -1;
37744                 this.onLoad();   
37745             }
37746         }
37747     },
37748
37749     // private
37750     getParams : function(q){
37751         var p = {};
37752         //p[this.queryParam] = q;
37753         if(this.pageSize){
37754             p.start = 0;
37755             p.limit = this.pageSize;
37756         }
37757         return p;
37758     },
37759
37760     /**
37761      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37762      */
37763     collapse : function(){
37764         if(!this.isExpanded()){
37765             return;
37766         }
37767         this.list.hide();
37768         Roo.get(document).un('mousedown', this.collapseIf, this);
37769         Roo.get(document).un('mousewheel', this.collapseIf, this);
37770         if (!this.editable) {
37771             Roo.get(document).un('keydown', this.listKeyPress, this);
37772         }
37773         this.fireEvent('collapse', this);
37774     },
37775
37776     // private
37777     collapseIf : function(e){
37778         if(!e.within(this.wrap) && !e.within(this.list)){
37779             this.collapse();
37780         }
37781     },
37782
37783     /**
37784      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37785      */
37786     expand : function(){
37787         if(this.isExpanded() || !this.hasFocus){
37788             return;
37789         }
37790         this.list.alignTo(this.el, this.listAlign);
37791         this.list.show();
37792         Roo.get(document).on('mousedown', this.collapseIf, this);
37793         Roo.get(document).on('mousewheel', this.collapseIf, this);
37794         if (!this.editable) {
37795             Roo.get(document).on('keydown', this.listKeyPress, this);
37796         }
37797         
37798         this.fireEvent('expand', this);
37799     },
37800
37801     // private
37802     // Implements the default empty TriggerField.onTriggerClick function
37803     onTriggerClick : function(){
37804         if(this.disabled){
37805             return;
37806         }
37807         if(this.isExpanded()){
37808             this.collapse();
37809             if (!this.blockFocus) {
37810                 this.el.focus();
37811             }
37812             
37813         }else {
37814             this.hasFocus = true;
37815             if(this.triggerAction == 'all') {
37816                 this.doQuery(this.allQuery, true);
37817             } else {
37818                 this.doQuery(this.getRawValue());
37819             }
37820             if (!this.blockFocus) {
37821                 this.el.focus();
37822             }
37823         }
37824     },
37825     listKeyPress : function(e)
37826     {
37827         //Roo.log('listkeypress');
37828         // scroll to first matching element based on key pres..
37829         if (e.isSpecialKey()) {
37830             return false;
37831         }
37832         var k = String.fromCharCode(e.getKey()).toUpperCase();
37833         //Roo.log(k);
37834         var match  = false;
37835         var csel = this.view.getSelectedNodes();
37836         var cselitem = false;
37837         if (csel.length) {
37838             var ix = this.view.indexOf(csel[0]);
37839             cselitem  = this.store.getAt(ix);
37840             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
37841                 cselitem = false;
37842             }
37843             
37844         }
37845         
37846         this.store.each(function(v) { 
37847             if (cselitem) {
37848                 // start at existing selection.
37849                 if (cselitem.id == v.id) {
37850                     cselitem = false;
37851                 }
37852                 return;
37853             }
37854                 
37855             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
37856                 match = this.store.indexOf(v);
37857                 return false;
37858             }
37859         }, this);
37860         
37861         if (match === false) {
37862             return true; // no more action?
37863         }
37864         // scroll to?
37865         this.view.select(match);
37866         var sn = Roo.get(this.view.getSelectedNodes()[0])
37867         sn.scrollIntoView(sn.dom.parentNode, false);
37868     }
37869
37870     /** 
37871     * @cfg {Boolean} grow 
37872     * @hide 
37873     */
37874     /** 
37875     * @cfg {Number} growMin 
37876     * @hide 
37877     */
37878     /** 
37879     * @cfg {Number} growMax 
37880     * @hide 
37881     */
37882     /**
37883      * @hide
37884      * @method autoSize
37885      */
37886 });/*
37887  * Based on:
37888  * Ext JS Library 1.1.1
37889  * Copyright(c) 2006-2007, Ext JS, LLC.
37890  *
37891  * Originally Released Under LGPL - original licence link has changed is not relivant.
37892  *
37893  * Fork - LGPL
37894  * <script type="text/javascript">
37895  */
37896 /**
37897  * @class Roo.form.Checkbox
37898  * @extends Roo.form.Field
37899  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37900  * @constructor
37901  * Creates a new Checkbox
37902  * @param {Object} config Configuration options
37903  */
37904 Roo.form.Checkbox = function(config){
37905     Roo.form.Checkbox.superclass.constructor.call(this, config);
37906     this.addEvents({
37907         /**
37908          * @event check
37909          * Fires when the checkbox is checked or unchecked.
37910              * @param {Roo.form.Checkbox} this This checkbox
37911              * @param {Boolean} checked The new checked value
37912              */
37913         check : true
37914     });
37915 };
37916
37917 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37918     /**
37919      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37920      */
37921     focusClass : undefined,
37922     /**
37923      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37924      */
37925     fieldClass: "x-form-field",
37926     /**
37927      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37928      */
37929     checked: false,
37930     /**
37931      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37932      * {tag: "input", type: "checkbox", autocomplete: "off"})
37933      */
37934     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37935     /**
37936      * @cfg {String} boxLabel The text that appears beside the checkbox
37937      */
37938     boxLabel : "",
37939     /**
37940      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37941      */  
37942     inputValue : '1',
37943     /**
37944      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37945      */
37946      valueOff: '0', // value when not checked..
37947
37948     actionMode : 'viewEl', 
37949     //
37950     // private
37951     itemCls : 'x-menu-check-item x-form-item',
37952     groupClass : 'x-menu-group-item',
37953     inputType : 'hidden',
37954     
37955     
37956     inSetChecked: false, // check that we are not calling self...
37957     
37958     inputElement: false, // real input element?
37959     basedOn: false, // ????
37960     
37961     isFormField: true, // not sure where this is needed!!!!
37962
37963     onResize : function(){
37964         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37965         if(!this.boxLabel){
37966             this.el.alignTo(this.wrap, 'c-c');
37967         }
37968     },
37969
37970     initEvents : function(){
37971         Roo.form.Checkbox.superclass.initEvents.call(this);
37972         this.el.on("click", this.onClick,  this);
37973         this.el.on("change", this.onClick,  this);
37974     },
37975
37976
37977     getResizeEl : function(){
37978         return this.wrap;
37979     },
37980
37981     getPositionEl : function(){
37982         return this.wrap;
37983     },
37984
37985     // private
37986     onRender : function(ct, position){
37987         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37988         /*
37989         if(this.inputValue !== undefined){
37990             this.el.dom.value = this.inputValue;
37991         }
37992         */
37993         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37994         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37995         var viewEl = this.wrap.createChild({ 
37996             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37997         this.viewEl = viewEl;   
37998         this.wrap.on('click', this.onClick,  this); 
37999         
38000         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38001         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38002         
38003         
38004         
38005         if(this.boxLabel){
38006             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38007         //    viewEl.on('click', this.onClick,  this); 
38008         }
38009         //if(this.checked){
38010             this.setChecked(this.checked);
38011         //}else{
38012             //this.checked = this.el.dom;
38013         //}
38014
38015     },
38016
38017     // private
38018     initValue : Roo.emptyFn,
38019
38020     /**
38021      * Returns the checked state of the checkbox.
38022      * @return {Boolean} True if checked, else false
38023      */
38024     getValue : function(){
38025         if(this.el){
38026             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38027         }
38028         return this.valueOff;
38029         
38030     },
38031
38032         // private
38033     onClick : function(){ 
38034         this.setChecked(!this.checked);
38035
38036         //if(this.el.dom.checked != this.checked){
38037         //    this.setValue(this.el.dom.checked);
38038        // }
38039     },
38040
38041     /**
38042      * Sets the checked state of the checkbox.
38043      * On is always based on a string comparison between inputValue and the param.
38044      * @param {Boolean/String} value - the value to set 
38045      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38046      */
38047     setValue : function(v,suppressEvent){
38048         
38049         
38050         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38051         //if(this.el && this.el.dom){
38052         //    this.el.dom.checked = this.checked;
38053         //    this.el.dom.defaultChecked = this.checked;
38054         //}
38055         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38056         //this.fireEvent("check", this, this.checked);
38057     },
38058     // private..
38059     setChecked : function(state,suppressEvent)
38060     {
38061         if (this.inSetChecked) {
38062             this.checked = state;
38063             return;
38064         }
38065         
38066     
38067         if(this.wrap){
38068             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38069         }
38070         this.checked = state;
38071         if(suppressEvent !== true){
38072             this.fireEvent('check', this, state);
38073         }
38074         this.inSetChecked = true;
38075         this.el.dom.value = state ? this.inputValue : this.valueOff;
38076         this.inSetChecked = false;
38077         
38078     },
38079     // handle setting of hidden value by some other method!!?!?
38080     setFromHidden: function()
38081     {
38082         if(!this.el){
38083             return;
38084         }
38085         //console.log("SET FROM HIDDEN");
38086         //alert('setFrom hidden');
38087         this.setValue(this.el.dom.value);
38088     },
38089     
38090     onDestroy : function()
38091     {
38092         if(this.viewEl){
38093             Roo.get(this.viewEl).remove();
38094         }
38095          
38096         Roo.form.Checkbox.superclass.onDestroy.call(this);
38097     }
38098
38099 });/*
38100  * Based on:
38101  * Ext JS Library 1.1.1
38102  * Copyright(c) 2006-2007, Ext JS, LLC.
38103  *
38104  * Originally Released Under LGPL - original licence link has changed is not relivant.
38105  *
38106  * Fork - LGPL
38107  * <script type="text/javascript">
38108  */
38109  
38110 /**
38111  * @class Roo.form.Radio
38112  * @extends Roo.form.Checkbox
38113  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38114  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38115  * @constructor
38116  * Creates a new Radio
38117  * @param {Object} config Configuration options
38118  */
38119 Roo.form.Radio = function(){
38120     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38121 };
38122 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38123     inputType: 'radio',
38124
38125     /**
38126      * If this radio is part of a group, it will return the selected value
38127      * @return {String}
38128      */
38129     getGroupValue : function(){
38130         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38131     }
38132 });//<script type="text/javascript">
38133
38134 /*
38135  * Ext JS Library 1.1.1
38136  * Copyright(c) 2006-2007, Ext JS, LLC.
38137  * licensing@extjs.com
38138  * 
38139  * http://www.extjs.com/license
38140  */
38141  
38142  /*
38143   * 
38144   * Known bugs:
38145   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38146   * - IE ? - no idea how much works there.
38147   * 
38148   * 
38149   * 
38150   */
38151  
38152
38153 /**
38154  * @class Ext.form.HtmlEditor
38155  * @extends Ext.form.Field
38156  * Provides a lightweight HTML Editor component.
38157  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38158  * 
38159  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38160  * supported by this editor.</b><br/><br/>
38161  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38162  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38163  */
38164 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38165       /**
38166      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38167      */
38168     toolbars : false,
38169     /**
38170      * @cfg {String} createLinkText The default text for the create link prompt
38171      */
38172     createLinkText : 'Please enter the URL for the link:',
38173     /**
38174      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38175      */
38176     defaultLinkValue : 'http:/'+'/',
38177    
38178     
38179     // id of frame..
38180     frameId: false,
38181     
38182     // private properties
38183     validationEvent : false,
38184     deferHeight: true,
38185     initialized : false,
38186     activated : false,
38187     sourceEditMode : false,
38188     onFocus : Roo.emptyFn,
38189     iframePad:3,
38190     hideMode:'offsets',
38191     defaultAutoCreate : {
38192         tag: "textarea",
38193         style:"width:500px;height:300px;",
38194         autocomplete: "off"
38195     },
38196
38197     // private
38198     initComponent : function(){
38199         this.addEvents({
38200             /**
38201              * @event initialize
38202              * Fires when the editor is fully initialized (including the iframe)
38203              * @param {HtmlEditor} this
38204              */
38205             initialize: true,
38206             /**
38207              * @event activate
38208              * Fires when the editor is first receives the focus. Any insertion must wait
38209              * until after this event.
38210              * @param {HtmlEditor} this
38211              */
38212             activate: true,
38213              /**
38214              * @event beforesync
38215              * Fires before the textarea is updated with content from the editor iframe. Return false
38216              * to cancel the sync.
38217              * @param {HtmlEditor} this
38218              * @param {String} html
38219              */
38220             beforesync: true,
38221              /**
38222              * @event beforepush
38223              * Fires before the iframe editor is updated with content from the textarea. Return false
38224              * to cancel the push.
38225              * @param {HtmlEditor} this
38226              * @param {String} html
38227              */
38228             beforepush: true,
38229              /**
38230              * @event sync
38231              * Fires when the textarea is updated with content from the editor iframe.
38232              * @param {HtmlEditor} this
38233              * @param {String} html
38234              */
38235             sync: true,
38236              /**
38237              * @event push
38238              * Fires when the iframe editor is updated with content from the textarea.
38239              * @param {HtmlEditor} this
38240              * @param {String} html
38241              */
38242             push: true,
38243              /**
38244              * @event editmodechange
38245              * Fires when the editor switches edit modes
38246              * @param {HtmlEditor} this
38247              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38248              */
38249             editmodechange: true,
38250             /**
38251              * @event editorevent
38252              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38253              * @param {HtmlEditor} this
38254              */
38255             editorevent: true
38256         })
38257     },
38258
38259     /**
38260      * Protected method that will not generally be called directly. It
38261      * is called when the editor creates its toolbar. Override this method if you need to
38262      * add custom toolbar buttons.
38263      * @param {HtmlEditor} editor
38264      */
38265     createToolbar : function(editor){
38266         if (!editor.toolbars || !editor.toolbars.length) {
38267             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38268         }
38269         
38270         for (var i =0 ; i < editor.toolbars.length;i++) {
38271             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38272             editor.toolbars[i].init(editor);
38273         }
38274          
38275         
38276     },
38277
38278     /**
38279      * Protected method that will not generally be called directly. It
38280      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38281      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38282      */
38283     getDocMarkup : function(){
38284         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38285     },
38286
38287     // private
38288     onRender : function(ct, position){
38289         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38290         this.el.dom.style.border = '0 none';
38291         this.el.dom.setAttribute('tabIndex', -1);
38292         this.el.addClass('x-hidden');
38293         if(Roo.isIE){ // fix IE 1px bogus margin
38294             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38295         }
38296         this.wrap = this.el.wrap({
38297             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38298         });
38299
38300         this.frameId = Roo.id();
38301         this.createToolbar(this);
38302         
38303         
38304         
38305         
38306       
38307         
38308         var iframe = this.wrap.createChild({
38309             tag: 'iframe',
38310             id: this.frameId,
38311             name: this.frameId,
38312             frameBorder : 'no',
38313             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38314         });
38315         
38316        // console.log(iframe);
38317         //this.wrap.dom.appendChild(iframe);
38318
38319         this.iframe = iframe.dom;
38320
38321          this.assignDocWin();
38322         
38323         this.doc.designMode = 'on';
38324        
38325         this.doc.open();
38326         this.doc.write(this.getDocMarkup());
38327         this.doc.close();
38328
38329         
38330         var task = { // must defer to wait for browser to be ready
38331             run : function(){
38332                 //console.log("run task?" + this.doc.readyState);
38333                 this.assignDocWin();
38334                 if(this.doc.body || this.doc.readyState == 'complete'){
38335                     try {
38336                         this.doc.designMode="on";
38337                     } catch (e) {
38338                         return;
38339                     }
38340                     Roo.TaskMgr.stop(task);
38341                     this.initEditor.defer(10, this);
38342                 }
38343             },
38344             interval : 10,
38345             duration:10000,
38346             scope: this
38347         };
38348         Roo.TaskMgr.start(task);
38349
38350         if(!this.width){
38351             this.setSize(this.el.getSize());
38352         }
38353     },
38354
38355     // private
38356     onResize : function(w, h){
38357         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38358         if(this.el && this.iframe){
38359             if(typeof w == 'number'){
38360                 var aw = w - this.wrap.getFrameWidth('lr');
38361                 this.el.setWidth(this.adjustWidth('textarea', aw));
38362                 this.iframe.style.width = aw + 'px';
38363             }
38364             if(typeof h == 'number'){
38365                 var tbh = 0;
38366                 for (var i =0; i < this.toolbars.length;i++) {
38367                     // fixme - ask toolbars for heights?
38368                     tbh += this.toolbars[i].tb.el.getHeight();
38369                 }
38370                 
38371                 
38372                 
38373                 
38374                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38375                 this.el.setHeight(this.adjustWidth('textarea', ah));
38376                 this.iframe.style.height = ah + 'px';
38377                 if(this.doc){
38378                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38379                 }
38380             }
38381         }
38382     },
38383
38384     /**
38385      * Toggles the editor between standard and source edit mode.
38386      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38387      */
38388     toggleSourceEdit : function(sourceEditMode){
38389         
38390         this.sourceEditMode = sourceEditMode === true;
38391         
38392         if(this.sourceEditMode){
38393           
38394             this.syncValue();
38395             this.iframe.className = 'x-hidden';
38396             this.el.removeClass('x-hidden');
38397             this.el.dom.removeAttribute('tabIndex');
38398             this.el.focus();
38399         }else{
38400              
38401             this.pushValue();
38402             this.iframe.className = '';
38403             this.el.addClass('x-hidden');
38404             this.el.dom.setAttribute('tabIndex', -1);
38405             this.deferFocus();
38406         }
38407         this.setSize(this.wrap.getSize());
38408         this.fireEvent('editmodechange', this, this.sourceEditMode);
38409     },
38410
38411     // private used internally
38412     createLink : function(){
38413         var url = prompt(this.createLinkText, this.defaultLinkValue);
38414         if(url && url != 'http:/'+'/'){
38415             this.relayCmd('createlink', url);
38416         }
38417     },
38418
38419     // private (for BoxComponent)
38420     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38421
38422     // private (for BoxComponent)
38423     getResizeEl : function(){
38424         return this.wrap;
38425     },
38426
38427     // private (for BoxComponent)
38428     getPositionEl : function(){
38429         return this.wrap;
38430     },
38431
38432     // private
38433     initEvents : function(){
38434         this.originalValue = this.getValue();
38435     },
38436
38437     /**
38438      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38439      * @method
38440      */
38441     markInvalid : Roo.emptyFn,
38442     /**
38443      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38444      * @method
38445      */
38446     clearInvalid : Roo.emptyFn,
38447
38448     setValue : function(v){
38449         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38450         this.pushValue();
38451     },
38452
38453     /**
38454      * Protected method that will not generally be called directly. If you need/want
38455      * custom HTML cleanup, this is the method you should override.
38456      * @param {String} html The HTML to be cleaned
38457      * return {String} The cleaned HTML
38458      */
38459     cleanHtml : function(html){
38460         html = String(html);
38461         if(html.length > 5){
38462             if(Roo.isSafari){ // strip safari nonsense
38463                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38464             }
38465         }
38466         if(html == '&nbsp;'){
38467             html = '';
38468         }
38469         return html;
38470     },
38471
38472     /**
38473      * Protected method that will not generally be called directly. Syncs the contents
38474      * of the editor iframe with the textarea.
38475      */
38476     syncValue : function(){
38477         if(this.initialized){
38478             var bd = (this.doc.body || this.doc.documentElement);
38479             var html = bd.innerHTML;
38480             if(Roo.isSafari){
38481                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38482                 var m = bs.match(/text-align:(.*?);/i);
38483                 if(m && m[1]){
38484                     html = '<div style="'+m[0]+'">' + html + '</div>';
38485                 }
38486             }
38487             html = this.cleanHtml(html);
38488             if(this.fireEvent('beforesync', this, html) !== false){
38489                 this.el.dom.value = html;
38490                 this.fireEvent('sync', this, html);
38491             }
38492         }
38493     },
38494
38495     /**
38496      * Protected method that will not generally be called directly. Pushes the value of the textarea
38497      * into the iframe editor.
38498      */
38499     pushValue : function(){
38500         if(this.initialized){
38501             var v = this.el.dom.value;
38502             if(v.length < 1){
38503                 v = '&#160;';
38504             }
38505             if(this.fireEvent('beforepush', this, v) !== false){
38506                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38507                 this.fireEvent('push', this, v);
38508             }
38509         }
38510     },
38511
38512     // private
38513     deferFocus : function(){
38514         this.focus.defer(10, this);
38515     },
38516
38517     // doc'ed in Field
38518     focus : function(){
38519         if(this.win && !this.sourceEditMode){
38520             this.win.focus();
38521         }else{
38522             this.el.focus();
38523         }
38524     },
38525     
38526     assignDocWin: function()
38527     {
38528         var iframe = this.iframe;
38529         
38530          if(Roo.isIE){
38531             this.doc = iframe.contentWindow.document;
38532             this.win = iframe.contentWindow;
38533         } else {
38534             if (!Roo.get(this.frameId)) {
38535                 return;
38536             }
38537             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38538             this.win = Roo.get(this.frameId).dom.contentWindow;
38539         }
38540     },
38541     
38542     // private
38543     initEditor : function(){
38544         //console.log("INIT EDITOR");
38545         this.assignDocWin();
38546         
38547         
38548         
38549         this.doc.designMode="on";
38550         this.doc.open();
38551         this.doc.write(this.getDocMarkup());
38552         this.doc.close();
38553         
38554         var dbody = (this.doc.body || this.doc.documentElement);
38555         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38556         // this copies styles from the containing element into thsi one..
38557         // not sure why we need all of this..
38558         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38559         ss['background-attachment'] = 'fixed'; // w3c
38560         dbody.bgProperties = 'fixed'; // ie
38561         Roo.DomHelper.applyStyles(dbody, ss);
38562         Roo.EventManager.on(this.doc, {
38563             'mousedown': this.onEditorEvent,
38564             'dblclick': this.onEditorEvent,
38565             'click': this.onEditorEvent,
38566             'keyup': this.onEditorEvent,
38567             buffer:100,
38568             scope: this
38569         });
38570         if(Roo.isGecko){
38571             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38572         }
38573         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38574             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38575         }
38576         this.initialized = true;
38577
38578         this.fireEvent('initialize', this);
38579         this.pushValue();
38580     },
38581
38582     // private
38583     onDestroy : function(){
38584         
38585         
38586         
38587         if(this.rendered){
38588             
38589             for (var i =0; i < this.toolbars.length;i++) {
38590                 // fixme - ask toolbars for heights?
38591                 this.toolbars[i].onDestroy();
38592             }
38593             
38594             this.wrap.dom.innerHTML = '';
38595             this.wrap.remove();
38596         }
38597     },
38598
38599     // private
38600     onFirstFocus : function(){
38601         
38602         this.assignDocWin();
38603         
38604         
38605         this.activated = true;
38606         for (var i =0; i < this.toolbars.length;i++) {
38607             this.toolbars[i].onFirstFocus();
38608         }
38609        
38610         if(Roo.isGecko){ // prevent silly gecko errors
38611             this.win.focus();
38612             var s = this.win.getSelection();
38613             if(!s.focusNode || s.focusNode.nodeType != 3){
38614                 var r = s.getRangeAt(0);
38615                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38616                 r.collapse(true);
38617                 this.deferFocus();
38618             }
38619             try{
38620                 this.execCmd('useCSS', true);
38621                 this.execCmd('styleWithCSS', false);
38622             }catch(e){}
38623         }
38624         this.fireEvent('activate', this);
38625     },
38626
38627     // private
38628     adjustFont: function(btn){
38629         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38630         //if(Roo.isSafari){ // safari
38631         //    adjust *= 2;
38632        // }
38633         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38634         if(Roo.isSafari){ // safari
38635             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38636             v =  (v < 10) ? 10 : v;
38637             v =  (v > 48) ? 48 : v;
38638             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38639             
38640         }
38641         
38642         
38643         v = Math.max(1, v+adjust);
38644         
38645         this.execCmd('FontSize', v  );
38646     },
38647
38648     onEditorEvent : function(e){
38649         this.fireEvent('editorevent', this, e);
38650       //  this.updateToolbar();
38651         this.syncValue();
38652     },
38653
38654     insertTag : function(tg)
38655     {
38656         // could be a bit smarter... -> wrap the current selected tRoo..
38657         
38658         this.execCmd("formatblock",   tg);
38659         
38660     },
38661     
38662     insertText : function(txt)
38663     {
38664         
38665         
38666         range = this.createRange();
38667         range.deleteContents();
38668                //alert(Sender.getAttribute('label'));
38669                
38670         range.insertNode(this.doc.createTextNode(txt));
38671     } ,
38672     
38673     // private
38674     relayBtnCmd : function(btn){
38675         this.relayCmd(btn.cmd);
38676     },
38677
38678     /**
38679      * Executes a Midas editor command on the editor document and performs necessary focus and
38680      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38681      * @param {String} cmd The Midas command
38682      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38683      */
38684     relayCmd : function(cmd, value){
38685         this.win.focus();
38686         this.execCmd(cmd, value);
38687         this.fireEvent('editorevent', this);
38688         //this.updateToolbar();
38689         this.deferFocus();
38690     },
38691
38692     /**
38693      * Executes a Midas editor command directly on the editor document.
38694      * For visual commands, you should use {@link #relayCmd} instead.
38695      * <b>This should only be called after the editor is initialized.</b>
38696      * @param {String} cmd The Midas command
38697      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38698      */
38699     execCmd : function(cmd, value){
38700         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38701         this.syncValue();
38702     },
38703
38704    
38705     /**
38706      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38707      * to insert tRoo.
38708      * @param {String} text
38709      */
38710     insertAtCursor : function(text){
38711         if(!this.activated){
38712             return;
38713         }
38714         if(Roo.isIE){
38715             this.win.focus();
38716             var r = this.doc.selection.createRange();
38717             if(r){
38718                 r.collapse(true);
38719                 r.pasteHTML(text);
38720                 this.syncValue();
38721                 this.deferFocus();
38722             }
38723         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38724             this.win.focus();
38725             this.execCmd('InsertHTML', text);
38726             this.deferFocus();
38727         }
38728     },
38729  // private
38730     mozKeyPress : function(e){
38731         if(e.ctrlKey){
38732             var c = e.getCharCode(), cmd;
38733           
38734             if(c > 0){
38735                 c = String.fromCharCode(c).toLowerCase();
38736                 switch(c){
38737                     case 'b':
38738                         cmd = 'bold';
38739                     break;
38740                     case 'i':
38741                         cmd = 'italic';
38742                     break;
38743                     case 'u':
38744                         cmd = 'underline';
38745                     case 'v':
38746                         this.cleanUpPaste.defer(100, this);
38747                         return;
38748                     break;
38749                 }
38750                 if(cmd){
38751                     this.win.focus();
38752                     this.execCmd(cmd);
38753                     this.deferFocus();
38754                     e.preventDefault();
38755                 }
38756                 
38757             }
38758         }
38759     },
38760
38761     // private
38762     fixKeys : function(){ // load time branching for fastest keydown performance
38763         if(Roo.isIE){
38764             return function(e){
38765                 var k = e.getKey(), r;
38766                 if(k == e.TAB){
38767                     e.stopEvent();
38768                     r = this.doc.selection.createRange();
38769                     if(r){
38770                         r.collapse(true);
38771                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38772                         this.deferFocus();
38773                     }
38774                     return;
38775                 }
38776                 
38777                 if(k == e.ENTER){
38778                     r = this.doc.selection.createRange();
38779                     if(r){
38780                         var target = r.parentElement();
38781                         if(!target || target.tagName.toLowerCase() != 'li'){
38782                             e.stopEvent();
38783                             r.pasteHTML('<br />');
38784                             r.collapse(false);
38785                             r.select();
38786                         }
38787                     }
38788                 }
38789                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38790                     this.cleanUpPaste.defer(100, this);
38791                     return;
38792                 }
38793                 
38794                 
38795             };
38796         }else if(Roo.isOpera){
38797             return function(e){
38798                 var k = e.getKey();
38799                 if(k == e.TAB){
38800                     e.stopEvent();
38801                     this.win.focus();
38802                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38803                     this.deferFocus();
38804                 }
38805                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38806                     this.cleanUpPaste.defer(100, this);
38807                     return;
38808                 }
38809                 
38810             };
38811         }else if(Roo.isSafari){
38812             return function(e){
38813                 var k = e.getKey();
38814                 
38815                 if(k == e.TAB){
38816                     e.stopEvent();
38817                     this.execCmd('InsertText','\t');
38818                     this.deferFocus();
38819                     return;
38820                 }
38821                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38822                     this.cleanUpPaste.defer(100, this);
38823                     return;
38824                 }
38825                 
38826              };
38827         }
38828     }(),
38829     
38830     getAllAncestors: function()
38831     {
38832         var p = this.getSelectedNode();
38833         var a = [];
38834         if (!p) {
38835             a.push(p); // push blank onto stack..
38836             p = this.getParentElement();
38837         }
38838         
38839         
38840         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38841             a.push(p);
38842             p = p.parentNode;
38843         }
38844         a.push(this.doc.body);
38845         return a;
38846     },
38847     lastSel : false,
38848     lastSelNode : false,
38849     
38850     
38851     getSelection : function() 
38852     {
38853         this.assignDocWin();
38854         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38855     },
38856     
38857     getSelectedNode: function() 
38858     {
38859         // this may only work on Gecko!!!
38860         
38861         // should we cache this!!!!
38862         
38863         
38864         
38865          
38866         var range = this.createRange(this.getSelection());
38867         
38868         if (Roo.isIE) {
38869             var parent = range.parentElement();
38870             while (true) {
38871                 var testRange = range.duplicate();
38872                 testRange.moveToElementText(parent);
38873                 if (testRange.inRange(range)) {
38874                     break;
38875                 }
38876                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38877                     break;
38878                 }
38879                 parent = parent.parentElement;
38880             }
38881             return parent;
38882         }
38883         
38884         
38885         var ar = range.endContainer.childNodes;
38886         if (!ar.length) {
38887             ar = range.commonAncestorContainer.childNodes;
38888             //alert(ar.length);
38889         }
38890         var nodes = [];
38891         var other_nodes = [];
38892         var has_other_nodes = false;
38893         for (var i=0;i<ar.length;i++) {
38894             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38895                 continue;
38896             }
38897             // fullly contained node.
38898             
38899             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38900                 nodes.push(ar[i]);
38901                 continue;
38902             }
38903             
38904             // probably selected..
38905             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38906                 other_nodes.push(ar[i]);
38907                 continue;
38908             }
38909             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38910                 continue;
38911             }
38912             
38913             
38914             has_other_nodes = true;
38915         }
38916         if (!nodes.length && other_nodes.length) {
38917             nodes= other_nodes;
38918         }
38919         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38920             return false;
38921         }
38922         
38923         return nodes[0];
38924     },
38925     createRange: function(sel)
38926     {
38927         // this has strange effects when using with 
38928         // top toolbar - not sure if it's a great idea.
38929         //this.editor.contentWindow.focus();
38930         if (typeof sel != "undefined") {
38931             try {
38932                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38933             } catch(e) {
38934                 return this.doc.createRange();
38935             }
38936         } else {
38937             return this.doc.createRange();
38938         }
38939     },
38940     getParentElement: function()
38941     {
38942         
38943         this.assignDocWin();
38944         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38945         
38946         var range = this.createRange(sel);
38947          
38948         try {
38949             var p = range.commonAncestorContainer;
38950             while (p.nodeType == 3) { // text node
38951                 p = p.parentNode;
38952             }
38953             return p;
38954         } catch (e) {
38955             return null;
38956         }
38957     
38958     },
38959     
38960     
38961     
38962     // BC Hacks - cause I cant work out what i was trying to do..
38963     rangeIntersectsNode : function(range, node)
38964     {
38965         var nodeRange = node.ownerDocument.createRange();
38966         try {
38967             nodeRange.selectNode(node);
38968         }
38969         catch (e) {
38970             nodeRange.selectNodeContents(node);
38971         }
38972
38973         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38974                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38975     },
38976     rangeCompareNode : function(range, node) {
38977         var nodeRange = node.ownerDocument.createRange();
38978         try {
38979             nodeRange.selectNode(node);
38980         } catch (e) {
38981             nodeRange.selectNodeContents(node);
38982         }
38983         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38984         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38985
38986         if (nodeIsBefore && !nodeIsAfter)
38987             return 0;
38988         if (!nodeIsBefore && nodeIsAfter)
38989             return 1;
38990         if (nodeIsBefore && nodeIsAfter)
38991             return 2;
38992
38993         return 3;
38994     },
38995
38996     // private? - in a new class?
38997     cleanUpPaste :  function()
38998     {
38999         // cleans up the whole document..
39000       //  console.log('cleanuppaste');
39001         this.cleanUpChildren(this.doc.body)
39002         
39003         
39004     },
39005     cleanUpChildren : function (n)
39006     {
39007         if (!n.childNodes.length) {
39008             return;
39009         }
39010         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39011            this.cleanUpChild(n.childNodes[i]);
39012         }
39013     },
39014     
39015     
39016         
39017     
39018     cleanUpChild : function (node)
39019     {
39020         //console.log(node);
39021         if (node.nodeName == "#text") {
39022             // clean up silly Windows -- stuff?
39023             return; 
39024         }
39025         if (node.nodeName == "#comment") {
39026             node.parentNode.removeChild(node);
39027             // clean up silly Windows -- stuff?
39028             return; 
39029         }
39030         
39031         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39032             // remove node.
39033             node.parentNode.removeChild(node);
39034             return;
39035             
39036         }
39037         if (!node.attributes || !node.attributes.length) {
39038             this.cleanUpChildren(node);
39039             return;
39040         }
39041         
39042         function cleanAttr(n,v)
39043         {
39044             
39045             if (v.match(/^\./) || v.match(/^\//)) {
39046                 return;
39047             }
39048             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39049                 return;
39050             }
39051             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39052             node.removeAttribute(n);
39053             
39054         }
39055         
39056         function cleanStyle(n,v)
39057         {
39058             if (v.match(/expression/)) { //XSS?? should we even bother..
39059                 node.removeAttribute(n);
39060                 return;
39061             }
39062             
39063             
39064             var parts = v.split(/;/);
39065             Roo.each(parts, function(p) {
39066                 p = p.replace(/\s+/g,'');
39067                 if (!p.length) {
39068                     return;
39069                 }
39070                 var l = p.split(':').shift().replace(/\s+/g,'');
39071                 
39072                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39073                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39074                     node.removeAttribute(n);
39075                     return false;
39076                 }
39077             });
39078             
39079             
39080         }
39081         
39082         
39083         for (var i = node.attributes.length-1; i > -1 ; i--) {
39084             var a = node.attributes[i];
39085             //console.log(a);
39086             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39087                 node.removeAttribute(a.name);
39088                 return;
39089             }
39090             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39091                 cleanAttr(a.name,a.value); // fixme..
39092                 return;
39093             }
39094             if (a.name == 'style') {
39095                 cleanStyle(a.name,a.value);
39096             }
39097             /// clean up MS crap..
39098             if (a.name == 'class') {
39099                 if (a.value.match(/^Mso/)) {
39100                     node.className = '';
39101                 }
39102             }
39103             
39104             // style cleanup!?
39105             // class cleanup?
39106             
39107         }
39108         
39109         
39110         this.cleanUpChildren(node);
39111         
39112         
39113     }
39114     
39115     
39116     // hide stuff that is not compatible
39117     /**
39118      * @event blur
39119      * @hide
39120      */
39121     /**
39122      * @event change
39123      * @hide
39124      */
39125     /**
39126      * @event focus
39127      * @hide
39128      */
39129     /**
39130      * @event specialkey
39131      * @hide
39132      */
39133     /**
39134      * @cfg {String} fieldClass @hide
39135      */
39136     /**
39137      * @cfg {String} focusClass @hide
39138      */
39139     /**
39140      * @cfg {String} autoCreate @hide
39141      */
39142     /**
39143      * @cfg {String} inputType @hide
39144      */
39145     /**
39146      * @cfg {String} invalidClass @hide
39147      */
39148     /**
39149      * @cfg {String} invalidText @hide
39150      */
39151     /**
39152      * @cfg {String} msgFx @hide
39153      */
39154     /**
39155      * @cfg {String} validateOnBlur @hide
39156      */
39157 });
39158
39159 Roo.form.HtmlEditor.white = [
39160         'area', 'br', 'img', 'input', 'hr', 'wbr',
39161         
39162        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39163        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39164        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39165        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39166        'table',   'ul',         'xmp', 
39167        
39168        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39169       'thead',   'tr', 
39170      
39171       'dir', 'menu', 'ol', 'ul', 'dl',
39172        
39173       'embed',  'object'
39174 ];
39175
39176
39177 Roo.form.HtmlEditor.black = [
39178     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39179         'applet', // 
39180         'base',   'basefont', 'bgsound', 'blink',  'body', 
39181         'frame',  'frameset', 'head',    'html',   'ilayer', 
39182         'iframe', 'layer',  'link',     'meta',    'object',   
39183         'script', 'style' ,'title',  'xml' // clean later..
39184 ];
39185 Roo.form.HtmlEditor.clean = [
39186     'script', 'style', 'title', 'xml'
39187 ];
39188
39189 // attributes..
39190
39191 Roo.form.HtmlEditor.ablack = [
39192     'on'
39193 ];
39194     
39195 Roo.form.HtmlEditor.aclean = [ 
39196     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39197 ];
39198
39199 // protocols..
39200 Roo.form.HtmlEditor.pwhite= [
39201         'http',  'https',  'mailto'
39202 ];
39203
39204 Roo.form.HtmlEditor.cwhite= [
39205         'text-align',
39206         'font-size'
39207 ];
39208
39209 // <script type="text/javascript">
39210 /*
39211  * Based on
39212  * Ext JS Library 1.1.1
39213  * Copyright(c) 2006-2007, Ext JS, LLC.
39214  *  
39215  
39216  */
39217
39218 /**
39219  * @class Roo.form.HtmlEditorToolbar1
39220  * Basic Toolbar
39221  * 
39222  * Usage:
39223  *
39224  new Roo.form.HtmlEditor({
39225     ....
39226     toolbars : [
39227         new Roo.form.HtmlEditorToolbar1({
39228             disable : { fonts: 1 , format: 1, ..., ... , ...],
39229             btns : [ .... ]
39230         })
39231     }
39232      
39233  * 
39234  * @cfg {Object} disable List of elements to disable..
39235  * @cfg {Array} btns List of additional buttons.
39236  * 
39237  * 
39238  * NEEDS Extra CSS? 
39239  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39240  */
39241  
39242 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39243 {
39244     
39245     Roo.apply(this, config);
39246     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39247     // dont call parent... till later.
39248 }
39249
39250 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39251     
39252     tb: false,
39253     
39254     rendered: false,
39255     
39256     editor : false,
39257     /**
39258      * @cfg {Object} disable  List of toolbar elements to disable
39259          
39260      */
39261     disable : false,
39262       /**
39263      * @cfg {Array} fontFamilies An array of available font families
39264      */
39265     fontFamilies : [
39266         'Arial',
39267         'Courier New',
39268         'Tahoma',
39269         'Times New Roman',
39270         'Verdana'
39271     ],
39272     
39273     specialChars : [
39274            "&#169;",
39275           "&#174;",     
39276           "&#8482;",    
39277           "&#163;" ,    
39278          // "&#8212;",    
39279           "&#8230;",    
39280           "&#247;" ,    
39281         //  "&#225;" ,     ?? a acute?
39282            "&#8364;"    , //Euro
39283        //   "&#8220;"    ,
39284         //  "&#8221;"    ,
39285         //  "&#8226;"    ,
39286           "&#176;"  //   , // degrees
39287
39288          // "&#233;"     , // e ecute
39289          // "&#250;"     , // u ecute?
39290     ],
39291     inputElements : [ 
39292             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39293             "input:submit", "input:button", "select", "textarea", "label" ],
39294     formats : [
39295         ["p"] ,  
39296         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39297         ["pre"],[ "code"], 
39298         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39299     ],
39300      /**
39301      * @cfg {String} defaultFont default font to use.
39302      */
39303     defaultFont: 'tahoma',
39304    
39305     fontSelect : false,
39306     
39307     
39308     formatCombo : false,
39309     
39310     init : function(editor)
39311     {
39312         this.editor = editor;
39313         
39314         
39315         var fid = editor.frameId;
39316         var etb = this;
39317         function btn(id, toggle, handler){
39318             var xid = fid + '-'+ id ;
39319             return {
39320                 id : xid,
39321                 cmd : id,
39322                 cls : 'x-btn-icon x-edit-'+id,
39323                 enableToggle:toggle !== false,
39324                 scope: editor, // was editor...
39325                 handler:handler||editor.relayBtnCmd,
39326                 clickEvent:'mousedown',
39327                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39328                 tabIndex:-1
39329             };
39330         }
39331         
39332         
39333         
39334         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39335         this.tb = tb;
39336          // stop form submits
39337         tb.el.on('click', function(e){
39338             e.preventDefault(); // what does this do?
39339         });
39340
39341         if(!this.disable.font && !Roo.isSafari){
39342             /* why no safari for fonts
39343             editor.fontSelect = tb.el.createChild({
39344                 tag:'select',
39345                 tabIndex: -1,
39346                 cls:'x-font-select',
39347                 html: editor.createFontOptions()
39348             });
39349             editor.fontSelect.on('change', function(){
39350                 var font = editor.fontSelect.dom.value;
39351                 editor.relayCmd('fontname', font);
39352                 editor.deferFocus();
39353             }, editor);
39354             tb.add(
39355                 editor.fontSelect.dom,
39356                 '-'
39357             );
39358             */
39359         };
39360         if(!this.disable.formats){
39361             this.formatCombo = new Roo.form.ComboBox({
39362                 store: new Roo.data.SimpleStore({
39363                     id : 'tag',
39364                     fields: ['tag'],
39365                     data : this.formats // from states.js
39366                 }),
39367                 blockFocus : true,
39368                 //autoCreate : {tag: "div",  size: "20"},
39369                 displayField:'tag',
39370                 typeAhead: false,
39371                 mode: 'local',
39372                 editable : false,
39373                 triggerAction: 'all',
39374                 emptyText:'Add tag',
39375                 selectOnFocus:true,
39376                 width:135,
39377                 listeners : {
39378                     'select': function(c, r, i) {
39379                         editor.insertTag(r.get('tag'));
39380                         editor.focus();
39381                     }
39382                 }
39383
39384             });
39385             tb.addField(this.formatCombo);
39386             
39387         }
39388         
39389         if(!this.disable.format){
39390             tb.add(
39391                 btn('bold'),
39392                 btn('italic'),
39393                 btn('underline')
39394             );
39395         };
39396         if(!this.disable.fontSize){
39397             tb.add(
39398                 '-',
39399                 
39400                 
39401                 btn('increasefontsize', false, editor.adjustFont),
39402                 btn('decreasefontsize', false, editor.adjustFont)
39403             );
39404         };
39405         
39406         
39407         if(this.disable.colors){
39408             tb.add(
39409                 '-', {
39410                     id:editor.frameId +'-forecolor',
39411                     cls:'x-btn-icon x-edit-forecolor',
39412                     clickEvent:'mousedown',
39413                     tooltip: this.buttonTips['forecolor'] || undefined,
39414                     tabIndex:-1,
39415                     menu : new Roo.menu.ColorMenu({
39416                         allowReselect: true,
39417                         focus: Roo.emptyFn,
39418                         value:'000000',
39419                         plain:true,
39420                         selectHandler: function(cp, color){
39421                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39422                             editor.deferFocus();
39423                         },
39424                         scope: editor,
39425                         clickEvent:'mousedown'
39426                     })
39427                 }, {
39428                     id:editor.frameId +'backcolor',
39429                     cls:'x-btn-icon x-edit-backcolor',
39430                     clickEvent:'mousedown',
39431                     tooltip: this.buttonTips['backcolor'] || undefined,
39432                     tabIndex:-1,
39433                     menu : new Roo.menu.ColorMenu({
39434                         focus: Roo.emptyFn,
39435                         value:'FFFFFF',
39436                         plain:true,
39437                         allowReselect: true,
39438                         selectHandler: function(cp, color){
39439                             if(Roo.isGecko){
39440                                 editor.execCmd('useCSS', false);
39441                                 editor.execCmd('hilitecolor', color);
39442                                 editor.execCmd('useCSS', true);
39443                                 editor.deferFocus();
39444                             }else{
39445                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39446                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39447                                 editor.deferFocus();
39448                             }
39449                         },
39450                         scope:editor,
39451                         clickEvent:'mousedown'
39452                     })
39453                 }
39454             );
39455         };
39456         // now add all the items...
39457         
39458
39459         if(!this.disable.alignments){
39460             tb.add(
39461                 '-',
39462                 btn('justifyleft'),
39463                 btn('justifycenter'),
39464                 btn('justifyright')
39465             );
39466         };
39467
39468         //if(!Roo.isSafari){
39469             if(!this.disable.links){
39470                 tb.add(
39471                     '-',
39472                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39473                 );
39474             };
39475
39476             if(!this.disable.lists){
39477                 tb.add(
39478                     '-',
39479                     btn('insertorderedlist'),
39480                     btn('insertunorderedlist')
39481                 );
39482             }
39483             if(!this.disable.sourceEdit){
39484                 tb.add(
39485                     '-',
39486                     btn('sourceedit', true, function(btn){
39487                         this.toggleSourceEdit(btn.pressed);
39488                     })
39489                 );
39490             }
39491         //}
39492         
39493         var smenu = { };
39494         // special menu.. - needs to be tidied up..
39495         if (!this.disable.special) {
39496             smenu = {
39497                 text: "&#169;",
39498                 cls: 'x-edit-none',
39499                 menu : {
39500                     items : []
39501                    }
39502             };
39503             for (var i =0; i < this.specialChars.length; i++) {
39504                 smenu.menu.items.push({
39505                     
39506                     html: this.specialChars[i],
39507                     handler: function(a,b) {
39508                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39509                         
39510                     },
39511                     tabIndex:-1
39512                 });
39513             }
39514             
39515             
39516             tb.add(smenu);
39517             
39518             
39519         }
39520         if (this.btns) {
39521             for(var i =0; i< this.btns.length;i++) {
39522                 var b = this.btns[i];
39523                 b.cls =  'x-edit-none';
39524                 b.scope = editor;
39525                 tb.add(b);
39526             }
39527         
39528         }
39529         
39530         
39531         
39532         // disable everything...
39533         
39534         this.tb.items.each(function(item){
39535            if(item.id != editor.frameId+ '-sourceedit'){
39536                 item.disable();
39537             }
39538         });
39539         this.rendered = true;
39540         
39541         // the all the btns;
39542         editor.on('editorevent', this.updateToolbar, this);
39543         // other toolbars need to implement this..
39544         //editor.on('editmodechange', this.updateToolbar, this);
39545     },
39546     
39547     
39548     
39549     /**
39550      * Protected method that will not generally be called directly. It triggers
39551      * a toolbar update by reading the markup state of the current selection in the editor.
39552      */
39553     updateToolbar: function(){
39554
39555         if(!this.editor.activated){
39556             this.editor.onFirstFocus();
39557             return;
39558         }
39559
39560         var btns = this.tb.items.map, 
39561             doc = this.editor.doc,
39562             frameId = this.editor.frameId;
39563
39564         if(!this.disable.font && !Roo.isSafari){
39565             /*
39566             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39567             if(name != this.fontSelect.dom.value){
39568                 this.fontSelect.dom.value = name;
39569             }
39570             */
39571         }
39572         if(!this.disable.format){
39573             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39574             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39575             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39576         }
39577         if(!this.disable.alignments){
39578             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39579             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39580             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39581         }
39582         if(!Roo.isSafari && !this.disable.lists){
39583             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39584             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39585         }
39586         
39587         var ans = this.editor.getAllAncestors();
39588         if (this.formatCombo) {
39589             
39590             
39591             var store = this.formatCombo.store;
39592             this.formatCombo.setValue("");
39593             for (var i =0; i < ans.length;i++) {
39594                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39595                     // select it..
39596                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39597                     break;
39598                 }
39599             }
39600         }
39601         
39602         
39603         
39604         // hides menus... - so this cant be on a menu...
39605         Roo.menu.MenuMgr.hideAll();
39606
39607         //this.editorsyncValue();
39608     },
39609    
39610     
39611     createFontOptions : function(){
39612         var buf = [], fs = this.fontFamilies, ff, lc;
39613         for(var i = 0, len = fs.length; i< len; i++){
39614             ff = fs[i];
39615             lc = ff.toLowerCase();
39616             buf.push(
39617                 '<option value="',lc,'" style="font-family:',ff,';"',
39618                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39619                     ff,
39620                 '</option>'
39621             );
39622         }
39623         return buf.join('');
39624     },
39625     
39626     toggleSourceEdit : function(sourceEditMode){
39627         if(sourceEditMode === undefined){
39628             sourceEditMode = !this.sourceEditMode;
39629         }
39630         this.sourceEditMode = sourceEditMode === true;
39631         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39632         // just toggle the button?
39633         if(btn.pressed !== this.editor.sourceEditMode){
39634             btn.toggle(this.editor.sourceEditMode);
39635             return;
39636         }
39637         
39638         if(this.sourceEditMode){
39639             this.tb.items.each(function(item){
39640                 if(item.cmd != 'sourceedit'){
39641                     item.disable();
39642                 }
39643             });
39644           
39645         }else{
39646             if(this.initialized){
39647                 this.tb.items.each(function(item){
39648                     item.enable();
39649                 });
39650             }
39651             
39652         }
39653         // tell the editor that it's been pressed..
39654         this.editor.toggleSourceEdit(sourceEditMode);
39655        
39656     },
39657      /**
39658      * Object collection of toolbar tooltips for the buttons in the editor. The key
39659      * is the command id associated with that button and the value is a valid QuickTips object.
39660      * For example:
39661 <pre><code>
39662 {
39663     bold : {
39664         title: 'Bold (Ctrl+B)',
39665         text: 'Make the selected text bold.',
39666         cls: 'x-html-editor-tip'
39667     },
39668     italic : {
39669         title: 'Italic (Ctrl+I)',
39670         text: 'Make the selected text italic.',
39671         cls: 'x-html-editor-tip'
39672     },
39673     ...
39674 </code></pre>
39675     * @type Object
39676      */
39677     buttonTips : {
39678         bold : {
39679             title: 'Bold (Ctrl+B)',
39680             text: 'Make the selected text bold.',
39681             cls: 'x-html-editor-tip'
39682         },
39683         italic : {
39684             title: 'Italic (Ctrl+I)',
39685             text: 'Make the selected text italic.',
39686             cls: 'x-html-editor-tip'
39687         },
39688         underline : {
39689             title: 'Underline (Ctrl+U)',
39690             text: 'Underline the selected text.',
39691             cls: 'x-html-editor-tip'
39692         },
39693         increasefontsize : {
39694             title: 'Grow Text',
39695             text: 'Increase the font size.',
39696             cls: 'x-html-editor-tip'
39697         },
39698         decreasefontsize : {
39699             title: 'Shrink Text',
39700             text: 'Decrease the font size.',
39701             cls: 'x-html-editor-tip'
39702         },
39703         backcolor : {
39704             title: 'Text Highlight Color',
39705             text: 'Change the background color of the selected text.',
39706             cls: 'x-html-editor-tip'
39707         },
39708         forecolor : {
39709             title: 'Font Color',
39710             text: 'Change the color of the selected text.',
39711             cls: 'x-html-editor-tip'
39712         },
39713         justifyleft : {
39714             title: 'Align Text Left',
39715             text: 'Align text to the left.',
39716             cls: 'x-html-editor-tip'
39717         },
39718         justifycenter : {
39719             title: 'Center Text',
39720             text: 'Center text in the editor.',
39721             cls: 'x-html-editor-tip'
39722         },
39723         justifyright : {
39724             title: 'Align Text Right',
39725             text: 'Align text to the right.',
39726             cls: 'x-html-editor-tip'
39727         },
39728         insertunorderedlist : {
39729             title: 'Bullet List',
39730             text: 'Start a bulleted list.',
39731             cls: 'x-html-editor-tip'
39732         },
39733         insertorderedlist : {
39734             title: 'Numbered List',
39735             text: 'Start a numbered list.',
39736             cls: 'x-html-editor-tip'
39737         },
39738         createlink : {
39739             title: 'Hyperlink',
39740             text: 'Make the selected text a hyperlink.',
39741             cls: 'x-html-editor-tip'
39742         },
39743         sourceedit : {
39744             title: 'Source Edit',
39745             text: 'Switch to source editing mode.',
39746             cls: 'x-html-editor-tip'
39747         }
39748     },
39749     // private
39750     onDestroy : function(){
39751         if(this.rendered){
39752             
39753             this.tb.items.each(function(item){
39754                 if(item.menu){
39755                     item.menu.removeAll();
39756                     if(item.menu.el){
39757                         item.menu.el.destroy();
39758                     }
39759                 }
39760                 item.destroy();
39761             });
39762              
39763         }
39764     },
39765     onFirstFocus: function() {
39766         this.tb.items.each(function(item){
39767            item.enable();
39768         });
39769     }
39770 });
39771
39772
39773
39774
39775 // <script type="text/javascript">
39776 /*
39777  * Based on
39778  * Ext JS Library 1.1.1
39779  * Copyright(c) 2006-2007, Ext JS, LLC.
39780  *  
39781  
39782  */
39783
39784  
39785 /**
39786  * @class Roo.form.HtmlEditor.ToolbarContext
39787  * Context Toolbar
39788  * 
39789  * Usage:
39790  *
39791  new Roo.form.HtmlEditor({
39792     ....
39793     toolbars : [
39794         new Roo.form.HtmlEditor.ToolbarStandard(),
39795         new Roo.form.HtmlEditor.ToolbarContext()
39796         })
39797     }
39798      
39799  * 
39800  * @config : {Object} disable List of elements to disable.. (not done yet.)
39801  * 
39802  * 
39803  */
39804
39805 Roo.form.HtmlEditor.ToolbarContext = function(config)
39806 {
39807     
39808     Roo.apply(this, config);
39809     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39810     // dont call parent... till later.
39811 }
39812 Roo.form.HtmlEditor.ToolbarContext.types = {
39813     'IMG' : {
39814         width : {
39815             title: "Width",
39816             width: 40
39817         },
39818         height:  {
39819             title: "Height",
39820             width: 40
39821         },
39822         align: {
39823             title: "Align",
39824             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39825             width : 80
39826             
39827         },
39828         border: {
39829             title: "Border",
39830             width: 40
39831         },
39832         alt: {
39833             title: "Alt",
39834             width: 120
39835         },
39836         src : {
39837             title: "Src",
39838             width: 220
39839         }
39840         
39841     },
39842     'A' : {
39843         name : {
39844             title: "Name",
39845             width: 50
39846         },
39847         href:  {
39848             title: "Href",
39849             width: 220
39850         } // border?
39851         
39852     },
39853     'TABLE' : {
39854         rows : {
39855             title: "Rows",
39856             width: 20
39857         },
39858         cols : {
39859             title: "Cols",
39860             width: 20
39861         },
39862         width : {
39863             title: "Width",
39864             width: 40
39865         },
39866         height : {
39867             title: "Height",
39868             width: 40
39869         },
39870         border : {
39871             title: "Border",
39872             width: 20
39873         }
39874     },
39875     'TD' : {
39876         width : {
39877             title: "Width",
39878             width: 40
39879         },
39880         height : {
39881             title: "Height",
39882             width: 40
39883         },   
39884         align: {
39885             title: "Align",
39886             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39887             width: 40
39888         },
39889         valign: {
39890             title: "Valign",
39891             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39892             width: 40
39893         },
39894         colspan: {
39895             title: "Colspan",
39896             width: 20
39897             
39898         }
39899     },
39900     'INPUT' : {
39901         name : {
39902             title: "name",
39903             width: 120
39904         },
39905         value : {
39906             title: "Value",
39907             width: 120
39908         },
39909         width : {
39910             title: "Width",
39911             width: 40
39912         }
39913     },
39914     'LABEL' : {
39915         'for' : {
39916             title: "For",
39917             width: 120
39918         }
39919     },
39920     'TEXTAREA' : {
39921           name : {
39922             title: "name",
39923             width: 120
39924         },
39925         rows : {
39926             title: "Rows",
39927             width: 20
39928         },
39929         cols : {
39930             title: "Cols",
39931             width: 20
39932         }
39933     },
39934     'SELECT' : {
39935         name : {
39936             title: "name",
39937             width: 120
39938         },
39939         selectoptions : {
39940             title: "Options",
39941             width: 200
39942         }
39943     },
39944     'BODY' : {
39945         title : {
39946             title: "title",
39947             width: 120,
39948             disabled : true
39949         }
39950     }
39951 };
39952
39953
39954
39955 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39956     
39957     tb: false,
39958     
39959     rendered: false,
39960     
39961     editor : false,
39962     /**
39963      * @cfg {Object} disable  List of toolbar elements to disable
39964          
39965      */
39966     disable : false,
39967     
39968     
39969     
39970     toolbars : false,
39971     
39972     init : function(editor)
39973     {
39974         this.editor = editor;
39975         
39976         
39977         var fid = editor.frameId;
39978         var etb = this;
39979         function btn(id, toggle, handler){
39980             var xid = fid + '-'+ id ;
39981             return {
39982                 id : xid,
39983                 cmd : id,
39984                 cls : 'x-btn-icon x-edit-'+id,
39985                 enableToggle:toggle !== false,
39986                 scope: editor, // was editor...
39987                 handler:handler||editor.relayBtnCmd,
39988                 clickEvent:'mousedown',
39989                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39990                 tabIndex:-1
39991             };
39992         }
39993         // create a new element.
39994         var wdiv = editor.wrap.createChild({
39995                 tag: 'div'
39996             }, editor.wrap.dom.firstChild.nextSibling, true);
39997         
39998         // can we do this more than once??
39999         
40000          // stop form submits
40001       
40002  
40003         // disable everything...
40004         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40005         this.toolbars = {};
40006            
40007         for (var i in  ty) {
40008           
40009             this.toolbars[i] = this.buildToolbar(ty[i],i);
40010         }
40011         this.tb = this.toolbars.BODY;
40012         this.tb.el.show();
40013         
40014          
40015         this.rendered = true;
40016         
40017         // the all the btns;
40018         editor.on('editorevent', this.updateToolbar, this);
40019         // other toolbars need to implement this..
40020         //editor.on('editmodechange', this.updateToolbar, this);
40021     },
40022     
40023     
40024     
40025     /**
40026      * Protected method that will not generally be called directly. It triggers
40027      * a toolbar update by reading the markup state of the current selection in the editor.
40028      */
40029     updateToolbar: function(){
40030
40031         if(!this.editor.activated){
40032             this.editor.onFirstFocus();
40033             return;
40034         }
40035
40036         
40037         var ans = this.editor.getAllAncestors();
40038         
40039         // pick
40040         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40041         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40042         sel = sel ? sel : this.editor.doc.body;
40043         sel = sel.tagName.length ? sel : this.editor.doc.body;
40044         var tn = sel.tagName.toUpperCase();
40045         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40046         tn = sel.tagName.toUpperCase();
40047         if (this.tb.name  == tn) {
40048             return; // no change
40049         }
40050         this.tb.el.hide();
40051         ///console.log("show: " + tn);
40052         this.tb =  this.toolbars[tn];
40053         this.tb.el.show();
40054         this.tb.fields.each(function(e) {
40055             e.setValue(sel.getAttribute(e.name));
40056         });
40057         this.tb.selectedNode = sel;
40058         
40059         
40060         Roo.menu.MenuMgr.hideAll();
40061
40062         //this.editorsyncValue();
40063     },
40064    
40065        
40066     // private
40067     onDestroy : function(){
40068         if(this.rendered){
40069             
40070             this.tb.items.each(function(item){
40071                 if(item.menu){
40072                     item.menu.removeAll();
40073                     if(item.menu.el){
40074                         item.menu.el.destroy();
40075                     }
40076                 }
40077                 item.destroy();
40078             });
40079              
40080         }
40081     },
40082     onFirstFocus: function() {
40083         // need to do this for all the toolbars..
40084         this.tb.items.each(function(item){
40085            item.enable();
40086         });
40087     },
40088     buildToolbar: function(tlist, nm)
40089     {
40090         var editor = this.editor;
40091          // create a new element.
40092         var wdiv = editor.wrap.createChild({
40093                 tag: 'div'
40094             }, editor.wrap.dom.firstChild.nextSibling, true);
40095         
40096        
40097         var tb = new Roo.Toolbar(wdiv);
40098         tb.add(nm+ ":&nbsp;");
40099         for (var i in tlist) {
40100             var item = tlist[i];
40101             tb.add(item.title + ":&nbsp;");
40102             if (item.opts) {
40103                 // fixme
40104                 
40105               
40106                 tb.addField( new Roo.form.ComboBox({
40107                     store: new Roo.data.SimpleStore({
40108                         id : 'val',
40109                         fields: ['val'],
40110                         data : item.opts // from states.js
40111                     }),
40112                     name : i,
40113                     displayField:'val',
40114                     typeAhead: false,
40115                     mode: 'local',
40116                     editable : false,
40117                     triggerAction: 'all',
40118                     emptyText:'Select',
40119                     selectOnFocus:true,
40120                     width: item.width ? item.width  : 130,
40121                     listeners : {
40122                         'select': function(c, r, i) {
40123                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40124                         }
40125                     }
40126
40127                 }));
40128                 continue;
40129                     
40130                 
40131                 
40132                 
40133                 
40134                 tb.addField( new Roo.form.TextField({
40135                     name: i,
40136                     width: 100,
40137                     //allowBlank:false,
40138                     value: ''
40139                 }));
40140                 continue;
40141             }
40142             tb.addField( new Roo.form.TextField({
40143                 name: i,
40144                 width: item.width,
40145                 //allowBlank:true,
40146                 value: '',
40147                 listeners: {
40148                     'change' : function(f, nv, ov) {
40149                         tb.selectedNode.setAttribute(f.name, nv);
40150                     }
40151                 }
40152             }));
40153              
40154         }
40155         tb.el.on('click', function(e){
40156             e.preventDefault(); // what does this do?
40157         });
40158         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40159         tb.el.hide();
40160         tb.name = nm;
40161         // dont need to disable them... as they will get hidden
40162         return tb;
40163          
40164         
40165     }
40166     
40167     
40168     
40169     
40170 });
40171
40172
40173
40174
40175
40176 /*
40177  * Based on:
40178  * Ext JS Library 1.1.1
40179  * Copyright(c) 2006-2007, Ext JS, LLC.
40180  *
40181  * Originally Released Under LGPL - original licence link has changed is not relivant.
40182  *
40183  * Fork - LGPL
40184  * <script type="text/javascript">
40185  */
40186  
40187 /**
40188  * @class Roo.form.BasicForm
40189  * @extends Roo.util.Observable
40190  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40191  * @constructor
40192  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40193  * @param {Object} config Configuration options
40194  */
40195 Roo.form.BasicForm = function(el, config){
40196     this.allItems = [];
40197     this.childForms = [];
40198     Roo.apply(this, config);
40199     /*
40200      * The Roo.form.Field items in this form.
40201      * @type MixedCollection
40202      */
40203      
40204      
40205     this.items = new Roo.util.MixedCollection(false, function(o){
40206         return o.id || (o.id = Roo.id());
40207     });
40208     this.addEvents({
40209         /**
40210          * @event beforeaction
40211          * Fires before any action is performed. Return false to cancel the action.
40212          * @param {Form} this
40213          * @param {Action} action The action to be performed
40214          */
40215         beforeaction: true,
40216         /**
40217          * @event actionfailed
40218          * Fires when an action fails.
40219          * @param {Form} this
40220          * @param {Action} action The action that failed
40221          */
40222         actionfailed : true,
40223         /**
40224          * @event actioncomplete
40225          * Fires when an action is completed.
40226          * @param {Form} this
40227          * @param {Action} action The action that completed
40228          */
40229         actioncomplete : true
40230     });
40231     if(el){
40232         this.initEl(el);
40233     }
40234     Roo.form.BasicForm.superclass.constructor.call(this);
40235 };
40236
40237 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40238     /**
40239      * @cfg {String} method
40240      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40241      */
40242     /**
40243      * @cfg {DataReader} reader
40244      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40245      * This is optional as there is built-in support for processing JSON.
40246      */
40247     /**
40248      * @cfg {DataReader} errorReader
40249      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40250      * This is completely optional as there is built-in support for processing JSON.
40251      */
40252     /**
40253      * @cfg {String} url
40254      * The URL to use for form actions if one isn't supplied in the action options.
40255      */
40256     /**
40257      * @cfg {Boolean} fileUpload
40258      * Set to true if this form is a file upload.
40259      */
40260     /**
40261      * @cfg {Object} baseParams
40262      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40263      */
40264     /**
40265      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40266      */
40267     timeout: 30,
40268
40269     // private
40270     activeAction : null,
40271
40272     /**
40273      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40274      * or setValues() data instead of when the form was first created.
40275      */
40276     trackResetOnLoad : false,
40277     
40278     
40279     /**
40280      * childForms - used for multi-tab forms
40281      * @type {Array}
40282      */
40283     childForms : false,
40284     
40285     /**
40286      * allItems - full list of fields.
40287      * @type {Array}
40288      */
40289     allItems : false,
40290     
40291     /**
40292      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40293      * element by passing it or its id or mask the form itself by passing in true.
40294      * @type Mixed
40295      */
40296     waitMsgTarget : undefined,
40297
40298     // private
40299     initEl : function(el){
40300         this.el = Roo.get(el);
40301         this.id = this.el.id || Roo.id();
40302         this.el.on('submit', this.onSubmit, this);
40303         this.el.addClass('x-form');
40304     },
40305
40306     // private
40307     onSubmit : function(e){
40308         e.stopEvent();
40309     },
40310
40311     /**
40312      * Returns true if client-side validation on the form is successful.
40313      * @return Boolean
40314      */
40315     isValid : function(){
40316         var valid = true;
40317         this.items.each(function(f){
40318            if(!f.validate()){
40319                valid = false;
40320            }
40321         });
40322         return valid;
40323     },
40324
40325     /**
40326      * Returns true if any fields in this form have changed since their original load.
40327      * @return Boolean
40328      */
40329     isDirty : function(){
40330         var dirty = false;
40331         this.items.each(function(f){
40332            if(f.isDirty()){
40333                dirty = true;
40334                return false;
40335            }
40336         });
40337         return dirty;
40338     },
40339
40340     /**
40341      * Performs a predefined action (submit or load) or custom actions you define on this form.
40342      * @param {String} actionName The name of the action type
40343      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40344      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40345      * accept other config options):
40346      * <pre>
40347 Property          Type             Description
40348 ----------------  ---------------  ----------------------------------------------------------------------------------
40349 url               String           The url for the action (defaults to the form's url)
40350 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40351 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40352 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40353                                    validate the form on the client (defaults to false)
40354      * </pre>
40355      * @return {BasicForm} this
40356      */
40357     doAction : function(action, options){
40358         if(typeof action == 'string'){
40359             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40360         }
40361         if(this.fireEvent('beforeaction', this, action) !== false){
40362             this.beforeAction(action);
40363             action.run.defer(100, action);
40364         }
40365         return this;
40366     },
40367
40368     /**
40369      * Shortcut to do a submit action.
40370      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40371      * @return {BasicForm} this
40372      */
40373     submit : function(options){
40374         this.doAction('submit', options);
40375         return this;
40376     },
40377
40378     /**
40379      * Shortcut to do a load action.
40380      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40381      * @return {BasicForm} this
40382      */
40383     load : function(options){
40384         this.doAction('load', options);
40385         return this;
40386     },
40387
40388     /**
40389      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40390      * @param {Record} record The record to edit
40391      * @return {BasicForm} this
40392      */
40393     updateRecord : function(record){
40394         record.beginEdit();
40395         var fs = record.fields;
40396         fs.each(function(f){
40397             var field = this.findField(f.name);
40398             if(field){
40399                 record.set(f.name, field.getValue());
40400             }
40401         }, this);
40402         record.endEdit();
40403         return this;
40404     },
40405
40406     /**
40407      * Loads an Roo.data.Record into this form.
40408      * @param {Record} record The record to load
40409      * @return {BasicForm} this
40410      */
40411     loadRecord : function(record){
40412         this.setValues(record.data);
40413         return this;
40414     },
40415
40416     // private
40417     beforeAction : function(action){
40418         var o = action.options;
40419         if(o.waitMsg){
40420             if(this.waitMsgTarget === true){
40421                 this.el.mask(o.waitMsg, 'x-mask-loading');
40422             }else if(this.waitMsgTarget){
40423                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40424                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
40425             }else{
40426                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
40427             }
40428         }
40429     },
40430
40431     // private
40432     afterAction : function(action, success){
40433         this.activeAction = null;
40434         var o = action.options;
40435         if(o.waitMsg){
40436             if(this.waitMsgTarget === true){
40437                 this.el.unmask();
40438             }else if(this.waitMsgTarget){
40439                 this.waitMsgTarget.unmask();
40440             }else{
40441                 Roo.MessageBox.updateProgress(1);
40442                 Roo.MessageBox.hide();
40443             }
40444         }
40445         if(success){
40446             if(o.reset){
40447                 this.reset();
40448             }
40449             Roo.callback(o.success, o.scope, [this, action]);
40450             this.fireEvent('actioncomplete', this, action);
40451         }else{
40452             Roo.callback(o.failure, o.scope, [this, action]);
40453             this.fireEvent('actionfailed', this, action);
40454         }
40455     },
40456
40457     /**
40458      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40459      * @param {String} id The value to search for
40460      * @return Field
40461      */
40462     findField : function(id){
40463         var field = this.items.get(id);
40464         if(!field){
40465             this.items.each(function(f){
40466                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40467                     field = f;
40468                     return false;
40469                 }
40470             });
40471         }
40472         return field || null;
40473     },
40474
40475     /**
40476      * Add a secondary form to this one, 
40477      * Used to provide tabbed forms. One form is primary, with hidden values 
40478      * which mirror the elements from the other forms.
40479      * 
40480      * @param {Roo.form.Form} form to add.
40481      * 
40482      */
40483     addForm : function(form)
40484     {
40485        
40486         if (this.childForms.indexOf(form) > -1) {
40487             // already added..
40488             return;
40489         }
40490         this.childForms.push(form);
40491         var n = '';
40492         Roo.each(form.allItems, function (fe) {
40493             
40494             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40495             if (this.findField(n)) { // already added..
40496                 return;
40497             }
40498             var add = new Roo.form.Hidden({
40499                 name : n
40500             });
40501             add.render(this.el);
40502             
40503             this.add( add );
40504         }, this);
40505         
40506     },
40507     /**
40508      * Mark fields in this form invalid in bulk.
40509      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40510      * @return {BasicForm} this
40511      */
40512     markInvalid : function(errors){
40513         if(errors instanceof Array){
40514             for(var i = 0, len = errors.length; i < len; i++){
40515                 var fieldError = errors[i];
40516                 var f = this.findField(fieldError.id);
40517                 if(f){
40518                     f.markInvalid(fieldError.msg);
40519                 }
40520             }
40521         }else{
40522             var field, id;
40523             for(id in errors){
40524                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40525                     field.markInvalid(errors[id]);
40526                 }
40527             }
40528         }
40529         Roo.each(this.childForms || [], function (f) {
40530             f.markInvalid(errors);
40531         });
40532         
40533         return this;
40534     },
40535
40536     /**
40537      * Set values for fields in this form in bulk.
40538      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40539      * @return {BasicForm} this
40540      */
40541     setValues : function(values){
40542         if(values instanceof Array){ // array of objects
40543             for(var i = 0, len = values.length; i < len; i++){
40544                 var v = values[i];
40545                 var f = this.findField(v.id);
40546                 if(f){
40547                     f.setValue(v.value);
40548                     if(this.trackResetOnLoad){
40549                         f.originalValue = f.getValue();
40550                     }
40551                 }
40552             }
40553         }else{ // object hash
40554             var field, id;
40555             for(id in values){
40556                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40557                     
40558                     if (field.setFromData && 
40559                         field.valueField && 
40560                         field.displayField &&
40561                         // combos' with local stores can 
40562                         // be queried via setValue()
40563                         // to set their value..
40564                         (field.store && !field.store.isLocal)
40565                         ) {
40566                         // it's a combo
40567                         var sd = { };
40568                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40569                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40570                         field.setFromData(sd);
40571                         
40572                     } else {
40573                         field.setValue(values[id]);
40574                     }
40575                     
40576                     
40577                     if(this.trackResetOnLoad){
40578                         field.originalValue = field.getValue();
40579                     }
40580                 }
40581             }
40582         }
40583          
40584         Roo.each(this.childForms || [], function (f) {
40585             f.setValues(values);
40586         });
40587                 
40588         return this;
40589     },
40590
40591     /**
40592      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40593      * they are returned as an array.
40594      * @param {Boolean} asString
40595      * @return {Object}
40596      */
40597     getValues : function(asString){
40598         if (this.childForms) {
40599             // copy values from the child forms
40600             Roo.each(this.childForms, function (f) {
40601                 this.setValues(f.getValues());
40602             }, this);
40603         }
40604         
40605         
40606         
40607         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40608         if(asString === true){
40609             return fs;
40610         }
40611         return Roo.urlDecode(fs);
40612     },
40613     
40614     /**
40615      * Returns the fields in this form as an object with key/value pairs. 
40616      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40617      * @return {Object}
40618      */
40619     getFieldValues : function()
40620     {
40621         if (this.childForms) {
40622             // copy values from the child forms
40623             Roo.each(this.childForms, function (f) {
40624                 this.setValues(f.getValues());
40625             }, this);
40626         }
40627         
40628         var ret = {};
40629         this.items.each(function(f){
40630             if (!f.getName()) {
40631                 return;
40632             }
40633             var v = f.getValue();
40634             if ((typeof(v) == 'object') && f.getRawValue) {
40635                 v = f.getRawValue() ; // dates..
40636             }
40637             ret[f.getName()] = v;
40638         });
40639         
40640         return ret;
40641     },
40642
40643     /**
40644      * Clears all invalid messages in this form.
40645      * @return {BasicForm} this
40646      */
40647     clearInvalid : function(){
40648         this.items.each(function(f){
40649            f.clearInvalid();
40650         });
40651         
40652         Roo.each(this.childForms || [], function (f) {
40653             f.clearInvalid();
40654         });
40655         
40656         
40657         return this;
40658     },
40659
40660     /**
40661      * Resets this form.
40662      * @return {BasicForm} this
40663      */
40664     reset : function(){
40665         this.items.each(function(f){
40666             f.reset();
40667         });
40668         
40669         Roo.each(this.childForms || [], function (f) {
40670             f.reset();
40671         });
40672        
40673         
40674         return this;
40675     },
40676
40677     /**
40678      * Add Roo.form components to this form.
40679      * @param {Field} field1
40680      * @param {Field} field2 (optional)
40681      * @param {Field} etc (optional)
40682      * @return {BasicForm} this
40683      */
40684     add : function(){
40685         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40686         return this;
40687     },
40688
40689
40690     /**
40691      * Removes a field from the items collection (does NOT remove its markup).
40692      * @param {Field} field
40693      * @return {BasicForm} this
40694      */
40695     remove : function(field){
40696         this.items.remove(field);
40697         return this;
40698     },
40699
40700     /**
40701      * Looks at the fields in this form, checks them for an id attribute,
40702      * and calls applyTo on the existing dom element with that id.
40703      * @return {BasicForm} this
40704      */
40705     render : function(){
40706         this.items.each(function(f){
40707             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40708                 f.applyTo(f.id);
40709             }
40710         });
40711         return this;
40712     },
40713
40714     /**
40715      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40716      * @param {Object} values
40717      * @return {BasicForm} this
40718      */
40719     applyToFields : function(o){
40720         this.items.each(function(f){
40721            Roo.apply(f, o);
40722         });
40723         return this;
40724     },
40725
40726     /**
40727      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40728      * @param {Object} values
40729      * @return {BasicForm} this
40730      */
40731     applyIfToFields : function(o){
40732         this.items.each(function(f){
40733            Roo.applyIf(f, o);
40734         });
40735         return this;
40736     }
40737 });
40738
40739 // back compat
40740 Roo.BasicForm = Roo.form.BasicForm;/*
40741  * Based on:
40742  * Ext JS Library 1.1.1
40743  * Copyright(c) 2006-2007, Ext JS, LLC.
40744  *
40745  * Originally Released Under LGPL - original licence link has changed is not relivant.
40746  *
40747  * Fork - LGPL
40748  * <script type="text/javascript">
40749  */
40750
40751 /**
40752  * @class Roo.form.Form
40753  * @extends Roo.form.BasicForm
40754  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40755  * @constructor
40756  * @param {Object} config Configuration options
40757  */
40758 Roo.form.Form = function(config){
40759     var xitems =  [];
40760     if (config.items) {
40761         xitems = config.items;
40762         delete config.items;
40763     }
40764    
40765     
40766     Roo.form.Form.superclass.constructor.call(this, null, config);
40767     this.url = this.url || this.action;
40768     if(!this.root){
40769         this.root = new Roo.form.Layout(Roo.applyIf({
40770             id: Roo.id()
40771         }, config));
40772     }
40773     this.active = this.root;
40774     /**
40775      * Array of all the buttons that have been added to this form via {@link addButton}
40776      * @type Array
40777      */
40778     this.buttons = [];
40779     this.allItems = [];
40780     this.addEvents({
40781         /**
40782          * @event clientvalidation
40783          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40784          * @param {Form} this
40785          * @param {Boolean} valid true if the form has passed client-side validation
40786          */
40787         clientvalidation: true,
40788         /**
40789          * @event rendered
40790          * Fires when the form is rendered
40791          * @param {Roo.form.Form} form
40792          */
40793         rendered : true
40794     });
40795     
40796     Roo.each(xitems, this.addxtype, this);
40797     
40798     
40799     
40800 };
40801
40802 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40803     /**
40804      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40805      */
40806     /**
40807      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40808      */
40809     /**
40810      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40811      */
40812     buttonAlign:'center',
40813
40814     /**
40815      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40816      */
40817     minButtonWidth:75,
40818
40819     /**
40820      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40821      * This property cascades to child containers if not set.
40822      */
40823     labelAlign:'left',
40824
40825     /**
40826      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40827      * fires a looping event with that state. This is required to bind buttons to the valid
40828      * state using the config value formBind:true on the button.
40829      */
40830     monitorValid : false,
40831
40832     /**
40833      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40834      */
40835     monitorPoll : 200,
40836
40837   
40838     /**
40839      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40840      * fields are added and the column is closed. If no fields are passed the column remains open
40841      * until end() is called.
40842      * @param {Object} config The config to pass to the column
40843      * @param {Field} field1 (optional)
40844      * @param {Field} field2 (optional)
40845      * @param {Field} etc (optional)
40846      * @return Column The column container object
40847      */
40848     column : function(c){
40849         var col = new Roo.form.Column(c);
40850         this.start(col);
40851         if(arguments.length > 1){ // duplicate code required because of Opera
40852             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40853             this.end();
40854         }
40855         return col;
40856     },
40857
40858     /**
40859      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40860      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40861      * until end() is called.
40862      * @param {Object} config The config to pass to the fieldset
40863      * @param {Field} field1 (optional)
40864      * @param {Field} field2 (optional)
40865      * @param {Field} etc (optional)
40866      * @return FieldSet The fieldset container object
40867      */
40868     fieldset : function(c){
40869         var fs = new Roo.form.FieldSet(c);
40870         this.start(fs);
40871         if(arguments.length > 1){ // duplicate code required because of Opera
40872             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40873             this.end();
40874         }
40875         return fs;
40876     },
40877
40878     /**
40879      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40880      * fields are added and the container is closed. If no fields are passed the container remains open
40881      * until end() is called.
40882      * @param {Object} config The config to pass to the Layout
40883      * @param {Field} field1 (optional)
40884      * @param {Field} field2 (optional)
40885      * @param {Field} etc (optional)
40886      * @return Layout The container object
40887      */
40888     container : function(c){
40889         var l = new Roo.form.Layout(c);
40890         this.start(l);
40891         if(arguments.length > 1){ // duplicate code required because of Opera
40892             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40893             this.end();
40894         }
40895         return l;
40896     },
40897
40898     /**
40899      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40900      * @param {Object} container A Roo.form.Layout or subclass of Layout
40901      * @return {Form} this
40902      */
40903     start : function(c){
40904         // cascade label info
40905         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40906         this.active.stack.push(c);
40907         c.ownerCt = this.active;
40908         this.active = c;
40909         return this;
40910     },
40911
40912     /**
40913      * Closes the current open container
40914      * @return {Form} this
40915      */
40916     end : function(){
40917         if(this.active == this.root){
40918             return this;
40919         }
40920         this.active = this.active.ownerCt;
40921         return this;
40922     },
40923
40924     /**
40925      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40926      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40927      * as the label of the field.
40928      * @param {Field} field1
40929      * @param {Field} field2 (optional)
40930      * @param {Field} etc. (optional)
40931      * @return {Form} this
40932      */
40933     add : function(){
40934         this.active.stack.push.apply(this.active.stack, arguments);
40935         this.allItems.push.apply(this.allItems,arguments);
40936         var r = [];
40937         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40938             if(a[i].isFormField){
40939                 r.push(a[i]);
40940             }
40941         }
40942         if(r.length > 0){
40943             Roo.form.Form.superclass.add.apply(this, r);
40944         }
40945         return this;
40946     },
40947     
40948
40949     
40950     
40951     
40952      /**
40953      * Find any element that has been added to a form, using it's ID or name
40954      * This can include framesets, columns etc. along with regular fields..
40955      * @param {String} id - id or name to find.
40956      
40957      * @return {Element} e - or false if nothing found.
40958      */
40959     findbyId : function(id)
40960     {
40961         var ret = false;
40962         if (!id) {
40963             return ret;
40964         }
40965         Ext.each(this.allItems, function(f){
40966             if (f.id == id || f.name == id ){
40967                 ret = f;
40968                 return false;
40969             }
40970         });
40971         return ret;
40972     },
40973
40974     
40975     
40976     /**
40977      * Render this form into the passed container. This should only be called once!
40978      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40979      * @return {Form} this
40980      */
40981     render : function(ct){
40982         ct = Roo.get(ct);
40983         var o = this.autoCreate || {
40984             tag: 'form',
40985             method : this.method || 'POST',
40986             id : this.id || Roo.id()
40987         };
40988         this.initEl(ct.createChild(o));
40989
40990         this.root.render(this.el);
40991
40992         this.items.each(function(f){
40993             f.render('x-form-el-'+f.id);
40994         });
40995
40996         if(this.buttons.length > 0){
40997             // tables are required to maintain order and for correct IE layout
40998             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40999                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41000                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41001             }}, null, true);
41002             var tr = tb.getElementsByTagName('tr')[0];
41003             for(var i = 0, len = this.buttons.length; i < len; i++) {
41004                 var b = this.buttons[i];
41005                 var td = document.createElement('td');
41006                 td.className = 'x-form-btn-td';
41007                 b.render(tr.appendChild(td));
41008             }
41009         }
41010         if(this.monitorValid){ // initialize after render
41011             this.startMonitoring();
41012         }
41013         this.fireEvent('rendered', this);
41014         return this;
41015     },
41016
41017     /**
41018      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41019      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41020      * object or a valid Roo.DomHelper element config
41021      * @param {Function} handler The function called when the button is clicked
41022      * @param {Object} scope (optional) The scope of the handler function
41023      * @return {Roo.Button}
41024      */
41025     addButton : function(config, handler, scope){
41026         var bc = {
41027             handler: handler,
41028             scope: scope,
41029             minWidth: this.minButtonWidth,
41030             hideParent:true
41031         };
41032         if(typeof config == "string"){
41033             bc.text = config;
41034         }else{
41035             Roo.apply(bc, config);
41036         }
41037         var btn = new Roo.Button(null, bc);
41038         this.buttons.push(btn);
41039         return btn;
41040     },
41041
41042      /**
41043      * Adds a series of form elements (using the xtype property as the factory method.
41044      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41045      * @param {Object} config 
41046      */
41047     
41048     addxtype : function()
41049     {
41050         var ar = Array.prototype.slice.call(arguments, 0);
41051         var ret = false;
41052         for(var i = 0; i < ar.length; i++) {
41053             if (!ar[i]) {
41054                 continue; // skip -- if this happends something invalid got sent, we 
41055                 // should ignore it, as basically that interface element will not show up
41056                 // and that should be pretty obvious!!
41057             }
41058             
41059             if (Roo.form[ar[i].xtype]) {
41060                 ar[i].form = this;
41061                 var fe = Roo.factory(ar[i], Roo.form);
41062                 if (!ret) {
41063                     ret = fe;
41064                 }
41065                 fe.form = this;
41066                 if (fe.store) {
41067                     fe.store.form = this;
41068                 }
41069                 if (fe.isLayout) {  
41070                          
41071                     this.start(fe);
41072                     this.allItems.push(fe);
41073                     if (fe.items && fe.addxtype) {
41074                         fe.addxtype.apply(fe, fe.items);
41075                         delete fe.items;
41076                     }
41077                      this.end();
41078                     continue;
41079                 }
41080                 
41081                 
41082                  
41083                 this.add(fe);
41084               //  console.log('adding ' + ar[i].xtype);
41085             }
41086             if (ar[i].xtype == 'Button') {  
41087                 //console.log('adding button');
41088                 //console.log(ar[i]);
41089                 this.addButton(ar[i]);
41090                 this.allItems.push(fe);
41091                 continue;
41092             }
41093             
41094             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41095                 alert('end is not supported on xtype any more, use items');
41096             //    this.end();
41097             //    //console.log('adding end');
41098             }
41099             
41100         }
41101         return ret;
41102     },
41103     
41104     /**
41105      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41106      * option "monitorValid"
41107      */
41108     startMonitoring : function(){
41109         if(!this.bound){
41110             this.bound = true;
41111             Roo.TaskMgr.start({
41112                 run : this.bindHandler,
41113                 interval : this.monitorPoll || 200,
41114                 scope: this
41115             });
41116         }
41117     },
41118
41119     /**
41120      * Stops monitoring of the valid state of this form
41121      */
41122     stopMonitoring : function(){
41123         this.bound = false;
41124     },
41125
41126     // private
41127     bindHandler : function(){
41128         if(!this.bound){
41129             return false; // stops binding
41130         }
41131         var valid = true;
41132         this.items.each(function(f){
41133             if(!f.isValid(true)){
41134                 valid = false;
41135                 return false;
41136             }
41137         });
41138         for(var i = 0, len = this.buttons.length; i < len; i++){
41139             var btn = this.buttons[i];
41140             if(btn.formBind === true && btn.disabled === valid){
41141                 btn.setDisabled(!valid);
41142             }
41143         }
41144         this.fireEvent('clientvalidation', this, valid);
41145     }
41146     
41147     
41148     
41149     
41150     
41151     
41152     
41153     
41154 });
41155
41156
41157 // back compat
41158 Roo.Form = Roo.form.Form;
41159 /*
41160  * Based on:
41161  * Ext JS Library 1.1.1
41162  * Copyright(c) 2006-2007, Ext JS, LLC.
41163  *
41164  * Originally Released Under LGPL - original licence link has changed is not relivant.
41165  *
41166  * Fork - LGPL
41167  * <script type="text/javascript">
41168  */
41169  
41170  /**
41171  * @class Roo.form.Action
41172  * Internal Class used to handle form actions
41173  * @constructor
41174  * @param {Roo.form.BasicForm} el The form element or its id
41175  * @param {Object} config Configuration options
41176  */
41177  
41178  
41179 // define the action interface
41180 Roo.form.Action = function(form, options){
41181     this.form = form;
41182     this.options = options || {};
41183 };
41184 /**
41185  * Client Validation Failed
41186  * @const 
41187  */
41188 Roo.form.Action.CLIENT_INVALID = 'client';
41189 /**
41190  * Server Validation Failed
41191  * @const 
41192  */
41193  Roo.form.Action.SERVER_INVALID = 'server';
41194  /**
41195  * Connect to Server Failed
41196  * @const 
41197  */
41198 Roo.form.Action.CONNECT_FAILURE = 'connect';
41199 /**
41200  * Reading Data from Server Failed
41201  * @const 
41202  */
41203 Roo.form.Action.LOAD_FAILURE = 'load';
41204
41205 Roo.form.Action.prototype = {
41206     type : 'default',
41207     failureType : undefined,
41208     response : undefined,
41209     result : undefined,
41210
41211     // interface method
41212     run : function(options){
41213
41214     },
41215
41216     // interface method
41217     success : function(response){
41218
41219     },
41220
41221     // interface method
41222     handleResponse : function(response){
41223
41224     },
41225
41226     // default connection failure
41227     failure : function(response){
41228         this.response = response;
41229         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41230         this.form.afterAction(this, false);
41231     },
41232
41233     processResponse : function(response){
41234         this.response = response;
41235         if(!response.responseText){
41236             return true;
41237         }
41238         this.result = this.handleResponse(response);
41239         return this.result;
41240     },
41241
41242     // utility functions used internally
41243     getUrl : function(appendParams){
41244         var url = this.options.url || this.form.url || this.form.el.dom.action;
41245         if(appendParams){
41246             var p = this.getParams();
41247             if(p){
41248                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41249             }
41250         }
41251         return url;
41252     },
41253
41254     getMethod : function(){
41255         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41256     },
41257
41258     getParams : function(){
41259         var bp = this.form.baseParams;
41260         var p = this.options.params;
41261         if(p){
41262             if(typeof p == "object"){
41263                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41264             }else if(typeof p == 'string' && bp){
41265                 p += '&' + Roo.urlEncode(bp);
41266             }
41267         }else if(bp){
41268             p = Roo.urlEncode(bp);
41269         }
41270         return p;
41271     },
41272
41273     createCallback : function(){
41274         return {
41275             success: this.success,
41276             failure: this.failure,
41277             scope: this,
41278             timeout: (this.form.timeout*1000),
41279             upload: this.form.fileUpload ? this.success : undefined
41280         };
41281     }
41282 };
41283
41284 Roo.form.Action.Submit = function(form, options){
41285     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41286 };
41287
41288 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41289     type : 'submit',
41290
41291     run : function()
41292     {
41293         // run get Values on the form, so it syncs any secondary forms.
41294         this.form.getValues();
41295         
41296         var o = this.options;
41297         var method = this.getMethod();
41298         var isPost = method == 'POST';
41299         if(o.clientValidation === false || this.form.isValid()){
41300             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41301                 form:this.form.el.dom,
41302                 url:this.getUrl(!isPost),
41303                 method: method,
41304                 params:isPost ? this.getParams() : null,
41305                 isUpload: this.form.fileUpload
41306             }));
41307
41308         }else if (o.clientValidation !== false){ // client validation failed
41309             this.failureType = Roo.form.Action.CLIENT_INVALID;
41310             this.form.afterAction(this, false);
41311         }
41312     },
41313
41314     success : function(response){
41315         var result = this.processResponse(response);
41316         if(result === true || result.success){
41317             this.form.afterAction(this, true);
41318             return;
41319         }
41320         if(result.errors){
41321             this.form.markInvalid(result.errors);
41322             this.failureType = Roo.form.Action.SERVER_INVALID;
41323         }
41324         this.form.afterAction(this, false);
41325     },
41326
41327     handleResponse : function(response){
41328         if(this.form.errorReader){
41329             var rs = this.form.errorReader.read(response);
41330             var errors = [];
41331             if(rs.records){
41332                 for(var i = 0, len = rs.records.length; i < len; i++) {
41333                     var r = rs.records[i];
41334                     errors[i] = r.data;
41335                 }
41336             }
41337             if(errors.length < 1){
41338                 errors = null;
41339             }
41340             return {
41341                 success : rs.success,
41342                 errors : errors
41343             };
41344         }
41345         var ret = false;
41346         try {
41347             ret = Roo.decode(response.responseText);
41348         } catch (e) {
41349             ret = {
41350                 success: false,
41351                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41352                 errors : []
41353             };
41354         }
41355         return ret;
41356         
41357     }
41358 });
41359
41360
41361 Roo.form.Action.Load = function(form, options){
41362     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41363     this.reader = this.form.reader;
41364 };
41365
41366 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41367     type : 'load',
41368
41369     run : function(){
41370         Roo.Ajax.request(Roo.apply(
41371                 this.createCallback(), {
41372                     method:this.getMethod(),
41373                     url:this.getUrl(false),
41374                     params:this.getParams()
41375         }));
41376     },
41377
41378     success : function(response){
41379         var result = this.processResponse(response);
41380         if(result === true || !result.success || !result.data){
41381             this.failureType = Roo.form.Action.LOAD_FAILURE;
41382             this.form.afterAction(this, false);
41383             return;
41384         }
41385         this.form.clearInvalid();
41386         this.form.setValues(result.data);
41387         this.form.afterAction(this, true);
41388     },
41389
41390     handleResponse : function(response){
41391         if(this.form.reader){
41392             var rs = this.form.reader.read(response);
41393             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41394             return {
41395                 success : rs.success,
41396                 data : data
41397             };
41398         }
41399         return Roo.decode(response.responseText);
41400     }
41401 });
41402
41403 Roo.form.Action.ACTION_TYPES = {
41404     'load' : Roo.form.Action.Load,
41405     'submit' : Roo.form.Action.Submit
41406 };/*
41407  * Based on:
41408  * Ext JS Library 1.1.1
41409  * Copyright(c) 2006-2007, Ext JS, LLC.
41410  *
41411  * Originally Released Under LGPL - original licence link has changed is not relivant.
41412  *
41413  * Fork - LGPL
41414  * <script type="text/javascript">
41415  */
41416  
41417 /**
41418  * @class Roo.form.Layout
41419  * @extends Roo.Component
41420  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41421  * @constructor
41422  * @param {Object} config Configuration options
41423  */
41424 Roo.form.Layout = function(config){
41425     var xitems = [];
41426     if (config.items) {
41427         xitems = config.items;
41428         delete config.items;
41429     }
41430     Roo.form.Layout.superclass.constructor.call(this, config);
41431     this.stack = [];
41432     Roo.each(xitems, this.addxtype, this);
41433      
41434 };
41435
41436 Roo.extend(Roo.form.Layout, Roo.Component, {
41437     /**
41438      * @cfg {String/Object} autoCreate
41439      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41440      */
41441     /**
41442      * @cfg {String/Object/Function} style
41443      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41444      * a function which returns such a specification.
41445      */
41446     /**
41447      * @cfg {String} labelAlign
41448      * Valid values are "left," "top" and "right" (defaults to "left")
41449      */
41450     /**
41451      * @cfg {Number} labelWidth
41452      * Fixed width in pixels of all field labels (defaults to undefined)
41453      */
41454     /**
41455      * @cfg {Boolean} clear
41456      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41457      */
41458     clear : true,
41459     /**
41460      * @cfg {String} labelSeparator
41461      * The separator to use after field labels (defaults to ':')
41462      */
41463     labelSeparator : ':',
41464     /**
41465      * @cfg {Boolean} hideLabels
41466      * True to suppress the display of field labels in this layout (defaults to false)
41467      */
41468     hideLabels : false,
41469
41470     // private
41471     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41472     
41473     isLayout : true,
41474     
41475     // private
41476     onRender : function(ct, position){
41477         if(this.el){ // from markup
41478             this.el = Roo.get(this.el);
41479         }else {  // generate
41480             var cfg = this.getAutoCreate();
41481             this.el = ct.createChild(cfg, position);
41482         }
41483         if(this.style){
41484             this.el.applyStyles(this.style);
41485         }
41486         if(this.labelAlign){
41487             this.el.addClass('x-form-label-'+this.labelAlign);
41488         }
41489         if(this.hideLabels){
41490             this.labelStyle = "display:none";
41491             this.elementStyle = "padding-left:0;";
41492         }else{
41493             if(typeof this.labelWidth == 'number'){
41494                 this.labelStyle = "width:"+this.labelWidth+"px;";
41495                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41496             }
41497             if(this.labelAlign == 'top'){
41498                 this.labelStyle = "width:auto;";
41499                 this.elementStyle = "padding-left:0;";
41500             }
41501         }
41502         var stack = this.stack;
41503         var slen = stack.length;
41504         if(slen > 0){
41505             if(!this.fieldTpl){
41506                 var t = new Roo.Template(
41507                     '<div class="x-form-item {5}">',
41508                         '<label for="{0}" style="{2}">{1}{4}</label>',
41509                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41510                         '</div>',
41511                     '</div><div class="x-form-clear-left"></div>'
41512                 );
41513                 t.disableFormats = true;
41514                 t.compile();
41515                 Roo.form.Layout.prototype.fieldTpl = t;
41516             }
41517             for(var i = 0; i < slen; i++) {
41518                 if(stack[i].isFormField){
41519                     this.renderField(stack[i]);
41520                 }else{
41521                     this.renderComponent(stack[i]);
41522                 }
41523             }
41524         }
41525         if(this.clear){
41526             this.el.createChild({cls:'x-form-clear'});
41527         }
41528     },
41529
41530     // private
41531     renderField : function(f){
41532         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41533                f.id, //0
41534                f.fieldLabel, //1
41535                f.labelStyle||this.labelStyle||'', //2
41536                this.elementStyle||'', //3
41537                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41538                f.itemCls||this.itemCls||''  //5
41539        ], true).getPrevSibling());
41540     },
41541
41542     // private
41543     renderComponent : function(c){
41544         c.render(c.isLayout ? this.el : this.el.createChild());    
41545     },
41546     /**
41547      * Adds a object form elements (using the xtype property as the factory method.)
41548      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41549      * @param {Object} config 
41550      */
41551     addxtype : function(o)
41552     {
41553         // create the lement.
41554         o.form = this.form;
41555         var fe = Roo.factory(o, Roo.form);
41556         this.form.allItems.push(fe);
41557         this.stack.push(fe);
41558         
41559         if (fe.isFormField) {
41560             this.form.items.add(fe);
41561         }
41562          
41563         return fe;
41564     }
41565 });
41566
41567 /**
41568  * @class Roo.form.Column
41569  * @extends Roo.form.Layout
41570  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41571  * @constructor
41572  * @param {Object} config Configuration options
41573  */
41574 Roo.form.Column = function(config){
41575     Roo.form.Column.superclass.constructor.call(this, config);
41576 };
41577
41578 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41579     /**
41580      * @cfg {Number/String} width
41581      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41582      */
41583     /**
41584      * @cfg {String/Object} autoCreate
41585      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41586      */
41587
41588     // private
41589     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41590
41591     // private
41592     onRender : function(ct, position){
41593         Roo.form.Column.superclass.onRender.call(this, ct, position);
41594         if(this.width){
41595             this.el.setWidth(this.width);
41596         }
41597     }
41598 });
41599
41600
41601 /**
41602  * @class Roo.form.Row
41603  * @extends Roo.form.Layout
41604  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41605  * @constructor
41606  * @param {Object} config Configuration options
41607  */
41608
41609  
41610 Roo.form.Row = function(config){
41611     Roo.form.Row.superclass.constructor.call(this, config);
41612 };
41613  
41614 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41615       /**
41616      * @cfg {Number/String} width
41617      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41618      */
41619     /**
41620      * @cfg {Number/String} height
41621      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41622      */
41623     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41624     
41625     padWidth : 20,
41626     // private
41627     onRender : function(ct, position){
41628         //console.log('row render');
41629         if(!this.rowTpl){
41630             var t = new Roo.Template(
41631                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41632                     '<label for="{0}" style="{2}">{1}{4}</label>',
41633                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41634                     '</div>',
41635                 '</div>'
41636             );
41637             t.disableFormats = true;
41638             t.compile();
41639             Roo.form.Layout.prototype.rowTpl = t;
41640         }
41641         this.fieldTpl = this.rowTpl;
41642         
41643         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41644         var labelWidth = 100;
41645         
41646         if ((this.labelAlign != 'top')) {
41647             if (typeof this.labelWidth == 'number') {
41648                 labelWidth = this.labelWidth
41649             }
41650             this.padWidth =  20 + labelWidth;
41651             
41652         }
41653         
41654         Roo.form.Column.superclass.onRender.call(this, ct, position);
41655         if(this.width){
41656             this.el.setWidth(this.width);
41657         }
41658         if(this.height){
41659             this.el.setHeight(this.height);
41660         }
41661     },
41662     
41663     // private
41664     renderField : function(f){
41665         f.fieldEl = this.fieldTpl.append(this.el, [
41666                f.id, f.fieldLabel,
41667                f.labelStyle||this.labelStyle||'',
41668                this.elementStyle||'',
41669                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41670                f.itemCls||this.itemCls||'',
41671                f.width ? f.width + this.padWidth : 160 + this.padWidth
41672        ],true);
41673     }
41674 });
41675  
41676
41677 /**
41678  * @class Roo.form.FieldSet
41679  * @extends Roo.form.Layout
41680  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41681  * @constructor
41682  * @param {Object} config Configuration options
41683  */
41684 Roo.form.FieldSet = function(config){
41685     Roo.form.FieldSet.superclass.constructor.call(this, config);
41686 };
41687
41688 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41689     /**
41690      * @cfg {String} legend
41691      * The text to display as the legend for the FieldSet (defaults to '')
41692      */
41693     /**
41694      * @cfg {String/Object} autoCreate
41695      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41696      */
41697
41698     // private
41699     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41700
41701     // private
41702     onRender : function(ct, position){
41703         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41704         if(this.legend){
41705             this.setLegend(this.legend);
41706         }
41707     },
41708
41709     // private
41710     setLegend : function(text){
41711         if(this.rendered){
41712             this.el.child('legend').update(text);
41713         }
41714     }
41715 });/*
41716  * Based on:
41717  * Ext JS Library 1.1.1
41718  * Copyright(c) 2006-2007, Ext JS, LLC.
41719  *
41720  * Originally Released Under LGPL - original licence link has changed is not relivant.
41721  *
41722  * Fork - LGPL
41723  * <script type="text/javascript">
41724  */
41725 /**
41726  * @class Roo.form.VTypes
41727  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41728  * @singleton
41729  */
41730 Roo.form.VTypes = function(){
41731     // closure these in so they are only created once.
41732     var alpha = /^[a-zA-Z_]+$/;
41733     var alphanum = /^[a-zA-Z0-9_]+$/;
41734     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41735     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41736
41737     // All these messages and functions are configurable
41738     return {
41739         /**
41740          * The function used to validate email addresses
41741          * @param {String} value The email address
41742          */
41743         'email' : function(v){
41744             return email.test(v);
41745         },
41746         /**
41747          * The error text to display when the email validation function returns false
41748          * @type String
41749          */
41750         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41751         /**
41752          * The keystroke filter mask to be applied on email input
41753          * @type RegExp
41754          */
41755         'emailMask' : /[a-z0-9_\.\-@]/i,
41756
41757         /**
41758          * The function used to validate URLs
41759          * @param {String} value The URL
41760          */
41761         'url' : function(v){
41762             return url.test(v);
41763         },
41764         /**
41765          * The error text to display when the url validation function returns false
41766          * @type String
41767          */
41768         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41769         
41770         /**
41771          * The function used to validate alpha values
41772          * @param {String} value The value
41773          */
41774         'alpha' : function(v){
41775             return alpha.test(v);
41776         },
41777         /**
41778          * The error text to display when the alpha validation function returns false
41779          * @type String
41780          */
41781         'alphaText' : 'This field should only contain letters and _',
41782         /**
41783          * The keystroke filter mask to be applied on alpha input
41784          * @type RegExp
41785          */
41786         'alphaMask' : /[a-z_]/i,
41787
41788         /**
41789          * The function used to validate alphanumeric values
41790          * @param {String} value The value
41791          */
41792         'alphanum' : function(v){
41793             return alphanum.test(v);
41794         },
41795         /**
41796          * The error text to display when the alphanumeric validation function returns false
41797          * @type String
41798          */
41799         'alphanumText' : 'This field should only contain letters, numbers and _',
41800         /**
41801          * The keystroke filter mask to be applied on alphanumeric input
41802          * @type RegExp
41803          */
41804         'alphanumMask' : /[a-z0-9_]/i
41805     };
41806 }();//<script type="text/javascript">
41807
41808 /**
41809  * @class Roo.form.FCKeditor
41810  * @extends Roo.form.TextArea
41811  * Wrapper around the FCKEditor http://www.fckeditor.net
41812  * @constructor
41813  * Creates a new FCKeditor
41814  * @param {Object} config Configuration options
41815  */
41816 Roo.form.FCKeditor = function(config){
41817     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41818     this.addEvents({
41819          /**
41820          * @event editorinit
41821          * Fired when the editor is initialized - you can add extra handlers here..
41822          * @param {FCKeditor} this
41823          * @param {Object} the FCK object.
41824          */
41825         editorinit : true
41826     });
41827     
41828     
41829 };
41830 Roo.form.FCKeditor.editors = { };
41831 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41832 {
41833     //defaultAutoCreate : {
41834     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41835     //},
41836     // private
41837     /**
41838      * @cfg {Object} fck options - see fck manual for details.
41839      */
41840     fckconfig : false,
41841     
41842     /**
41843      * @cfg {Object} fck toolbar set (Basic or Default)
41844      */
41845     toolbarSet : 'Basic',
41846     /**
41847      * @cfg {Object} fck BasePath
41848      */ 
41849     basePath : '/fckeditor/',
41850     
41851     
41852     frame : false,
41853     
41854     value : '',
41855     
41856    
41857     onRender : function(ct, position)
41858     {
41859         if(!this.el){
41860             this.defaultAutoCreate = {
41861                 tag: "textarea",
41862                 style:"width:300px;height:60px;",
41863                 autocomplete: "off"
41864             };
41865         }
41866         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41867         /*
41868         if(this.grow){
41869             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41870             if(this.preventScrollbars){
41871                 this.el.setStyle("overflow", "hidden");
41872             }
41873             this.el.setHeight(this.growMin);
41874         }
41875         */
41876         //console.log('onrender' + this.getId() );
41877         Roo.form.FCKeditor.editors[this.getId()] = this;
41878          
41879
41880         this.replaceTextarea() ;
41881         
41882     },
41883     
41884     getEditor : function() {
41885         return this.fckEditor;
41886     },
41887     /**
41888      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41889      * @param {Mixed} value The value to set
41890      */
41891     
41892     
41893     setValue : function(value)
41894     {
41895         //console.log('setValue: ' + value);
41896         
41897         if(typeof(value) == 'undefined') { // not sure why this is happending...
41898             return;
41899         }
41900         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41901         
41902         //if(!this.el || !this.getEditor()) {
41903         //    this.value = value;
41904             //this.setValue.defer(100,this,[value]);    
41905         //    return;
41906         //} 
41907         
41908         if(!this.getEditor()) {
41909             return;
41910         }
41911         
41912         this.getEditor().SetData(value);
41913         
41914         //
41915
41916     },
41917
41918     /**
41919      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41920      * @return {Mixed} value The field value
41921      */
41922     getValue : function()
41923     {
41924         
41925         if (this.frame && this.frame.dom.style.display == 'none') {
41926             return Roo.form.FCKeditor.superclass.getValue.call(this);
41927         }
41928         
41929         if(!this.el || !this.getEditor()) {
41930            
41931            // this.getValue.defer(100,this); 
41932             return this.value;
41933         }
41934        
41935         
41936         var value=this.getEditor().GetData();
41937         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41938         return Roo.form.FCKeditor.superclass.getValue.call(this);
41939         
41940
41941     },
41942
41943     /**
41944      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41945      * @return {Mixed} value The field value
41946      */
41947     getRawValue : function()
41948     {
41949         if (this.frame && this.frame.dom.style.display == 'none') {
41950             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41951         }
41952         
41953         if(!this.el || !this.getEditor()) {
41954             //this.getRawValue.defer(100,this); 
41955             return this.value;
41956             return;
41957         }
41958         
41959         
41960         
41961         var value=this.getEditor().GetData();
41962         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41963         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41964          
41965     },
41966     
41967     setSize : function(w,h) {
41968         
41969         
41970         
41971         //if (this.frame && this.frame.dom.style.display == 'none') {
41972         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41973         //    return;
41974         //}
41975         //if(!this.el || !this.getEditor()) {
41976         //    this.setSize.defer(100,this, [w,h]); 
41977         //    return;
41978         //}
41979         
41980         
41981         
41982         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41983         
41984         this.frame.dom.setAttribute('width', w);
41985         this.frame.dom.setAttribute('height', h);
41986         this.frame.setSize(w,h);
41987         
41988     },
41989     
41990     toggleSourceEdit : function(value) {
41991         
41992       
41993          
41994         this.el.dom.style.display = value ? '' : 'none';
41995         this.frame.dom.style.display = value ?  'none' : '';
41996         
41997     },
41998     
41999     
42000     focus: function(tag)
42001     {
42002         if (this.frame.dom.style.display == 'none') {
42003             return Roo.form.FCKeditor.superclass.focus.call(this);
42004         }
42005         if(!this.el || !this.getEditor()) {
42006             this.focus.defer(100,this, [tag]); 
42007             return;
42008         }
42009         
42010         
42011         
42012         
42013         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42014         this.getEditor().Focus();
42015         if (tgs.length) {
42016             if (!this.getEditor().Selection.GetSelection()) {
42017                 this.focus.defer(100,this, [tag]); 
42018                 return;
42019             }
42020             
42021             
42022             var r = this.getEditor().EditorDocument.createRange();
42023             r.setStart(tgs[0],0);
42024             r.setEnd(tgs[0],0);
42025             this.getEditor().Selection.GetSelection().removeAllRanges();
42026             this.getEditor().Selection.GetSelection().addRange(r);
42027             this.getEditor().Focus();
42028         }
42029         
42030     },
42031     
42032     
42033     
42034     replaceTextarea : function()
42035     {
42036         if ( document.getElementById( this.getId() + '___Frame' ) )
42037             return ;
42038         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42039         //{
42040             // We must check the elements firstly using the Id and then the name.
42041         var oTextarea = document.getElementById( this.getId() );
42042         
42043         var colElementsByName = document.getElementsByName( this.getId() ) ;
42044          
42045         oTextarea.style.display = 'none' ;
42046
42047         if ( oTextarea.tabIndex ) {            
42048             this.TabIndex = oTextarea.tabIndex ;
42049         }
42050         
42051         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42052         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42053         this.frame = Roo.get(this.getId() + '___Frame')
42054     },
42055     
42056     _getConfigHtml : function()
42057     {
42058         var sConfig = '' ;
42059
42060         for ( var o in this.fckconfig ) {
42061             sConfig += sConfig.length > 0  ? '&amp;' : '';
42062             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42063         }
42064
42065         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42066     },
42067     
42068     
42069     _getIFrameHtml : function()
42070     {
42071         var sFile = 'fckeditor.html' ;
42072         /* no idea what this is about..
42073         try
42074         {
42075             if ( (/fcksource=true/i).test( window.top.location.search ) )
42076                 sFile = 'fckeditor.original.html' ;
42077         }
42078         catch (e) { 
42079         */
42080
42081         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42082         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42083         
42084         
42085         var html = '<iframe id="' + this.getId() +
42086             '___Frame" src="' + sLink +
42087             '" width="' + this.width +
42088             '" height="' + this.height + '"' +
42089             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42090             ' frameborder="0" scrolling="no"></iframe>' ;
42091
42092         return html ;
42093     },
42094     
42095     _insertHtmlBefore : function( html, element )
42096     {
42097         if ( element.insertAdjacentHTML )       {
42098             // IE
42099             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42100         } else { // Gecko
42101             var oRange = document.createRange() ;
42102             oRange.setStartBefore( element ) ;
42103             var oFragment = oRange.createContextualFragment( html );
42104             element.parentNode.insertBefore( oFragment, element ) ;
42105         }
42106     }
42107     
42108     
42109   
42110     
42111     
42112     
42113     
42114
42115 });
42116
42117 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42118
42119 function FCKeditor_OnComplete(editorInstance){
42120     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42121     f.fckEditor = editorInstance;
42122     //console.log("loaded");
42123     f.fireEvent('editorinit', f, editorInstance);
42124
42125   
42126
42127  
42128
42129
42130
42131
42132
42133
42134
42135
42136
42137
42138
42139
42140
42141
42142
42143 //<script type="text/javascript">
42144 /**
42145  * @class Roo.form.GridField
42146  * @extends Roo.form.Field
42147  * Embed a grid (or editable grid into a form)
42148  * STATUS ALPHA
42149  * 
42150  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42151  * it needs 
42152  * xgrid.store = Roo.data.Store
42153  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42154  * xgrid.store.reader = Roo.data.JsonReader 
42155  * 
42156  * 
42157  * @constructor
42158  * Creates a new GridField
42159  * @param {Object} config Configuration options
42160  */
42161 Roo.form.GridField = function(config){
42162     Roo.form.GridField.superclass.constructor.call(this, config);
42163      
42164 };
42165
42166 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42167     /**
42168      * @cfg {Number} width  - used to restrict width of grid..
42169      */
42170     width : 100,
42171     /**
42172      * @cfg {Number} height - used to restrict height of grid..
42173      */
42174     height : 50,
42175      /**
42176      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42177          * 
42178          *}
42179      */
42180     xgrid : false, 
42181     /**
42182      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42183      * {tag: "input", type: "checkbox", autocomplete: "off"})
42184      */
42185    // defaultAutoCreate : { tag: 'div' },
42186     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42187     /**
42188      * @cfg {String} addTitle Text to include for adding a title.
42189      */
42190     addTitle : false,
42191     //
42192     onResize : function(){
42193         Roo.form.Field.superclass.onResize.apply(this, arguments);
42194     },
42195
42196     initEvents : function(){
42197         // Roo.form.Checkbox.superclass.initEvents.call(this);
42198         // has no events...
42199        
42200     },
42201
42202
42203     getResizeEl : function(){
42204         return this.wrap;
42205     },
42206
42207     getPositionEl : function(){
42208         return this.wrap;
42209     },
42210
42211     // private
42212     onRender : function(ct, position){
42213         
42214         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42215         var style = this.style;
42216         delete this.style;
42217         
42218         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42219         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42220         this.viewEl = this.wrap.createChild({ tag: 'div' });
42221         if (style) {
42222             this.viewEl.applyStyles(style);
42223         }
42224         if (this.width) {
42225             this.viewEl.setWidth(this.width);
42226         }
42227         if (this.height) {
42228             this.viewEl.setHeight(this.height);
42229         }
42230         //if(this.inputValue !== undefined){
42231         //this.setValue(this.value);
42232         
42233         
42234         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42235         
42236         
42237         this.grid.render();
42238         this.grid.getDataSource().on('remove', this.refreshValue, this);
42239         this.grid.getDataSource().on('update', this.refreshValue, this);
42240         this.grid.on('afteredit', this.refreshValue, this);
42241  
42242     },
42243      
42244     
42245     /**
42246      * Sets the value of the item. 
42247      * @param {String} either an object  or a string..
42248      */
42249     setValue : function(v){
42250         //this.value = v;
42251         v = v || []; // empty set..
42252         // this does not seem smart - it really only affects memoryproxy grids..
42253         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42254             var ds = this.grid.getDataSource();
42255             // assumes a json reader..
42256             var data = {}
42257             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42258             ds.loadData( data);
42259         }
42260         Roo.form.GridField.superclass.setValue.call(this, v);
42261         this.refreshValue();
42262         // should load data in the grid really....
42263     },
42264     
42265     // private
42266     refreshValue: function() {
42267          var val = [];
42268         this.grid.getDataSource().each(function(r) {
42269             val.push(r.data);
42270         });
42271         this.el.dom.value = Roo.encode(val);
42272     }
42273     
42274      
42275     
42276     
42277 });/*
42278  * Based on:
42279  * Ext JS Library 1.1.1
42280  * Copyright(c) 2006-2007, Ext JS, LLC.
42281  *
42282  * Originally Released Under LGPL - original licence link has changed is not relivant.
42283  *
42284  * Fork - LGPL
42285  * <script type="text/javascript">
42286  */
42287 /**
42288  * @class Roo.form.DisplayField
42289  * @extends Roo.form.Field
42290  * A generic Field to display non-editable data.
42291  * @constructor
42292  * Creates a new Display Field item.
42293  * @param {Object} config Configuration options
42294  */
42295 Roo.form.DisplayField = function(config){
42296     Roo.form.DisplayField.superclass.constructor.call(this, config);
42297     
42298 };
42299
42300 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42301     inputType:      'hidden',
42302     allowBlank:     true,
42303     readOnly:         true,
42304     
42305  
42306     /**
42307      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42308      */
42309     focusClass : undefined,
42310     /**
42311      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42312      */
42313     fieldClass: 'x-form-field',
42314     
42315      /**
42316      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42317      */
42318     valueRenderer: undefined,
42319     
42320     width: 100,
42321     /**
42322      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42323      * {tag: "input", type: "checkbox", autocomplete: "off"})
42324      */
42325      
42326  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42327
42328     onResize : function(){
42329         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42330         
42331     },
42332
42333     initEvents : function(){
42334         // Roo.form.Checkbox.superclass.initEvents.call(this);
42335         // has no events...
42336        
42337     },
42338
42339
42340     getResizeEl : function(){
42341         return this.wrap;
42342     },
42343
42344     getPositionEl : function(){
42345         return this.wrap;
42346     },
42347
42348     // private
42349     onRender : function(ct, position){
42350         
42351         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42352         //if(this.inputValue !== undefined){
42353         this.wrap = this.el.wrap();
42354         
42355         this.viewEl = this.wrap.createChild({ tag: 'div'});
42356         
42357         if (this.bodyStyle) {
42358             this.viewEl.applyStyles(this.bodyStyle);
42359         }
42360         //this.viewEl.setStyle('padding', '2px');
42361         
42362         this.setValue(this.value);
42363         
42364     },
42365 /*
42366     // private
42367     initValue : Roo.emptyFn,
42368
42369   */
42370
42371         // private
42372     onClick : function(){
42373         
42374     },
42375
42376     /**
42377      * Sets the checked state of the checkbox.
42378      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42379      */
42380     setValue : function(v){
42381         this.value = v;
42382         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42383         // this might be called before we have a dom element..
42384         if (!this.viewEl) {
42385             return;
42386         }
42387         this.viewEl.dom.innerHTML = html;
42388         Roo.form.DisplayField.superclass.setValue.call(this, v);
42389
42390     }
42391 });//<script type="text/javasscript">
42392  
42393
42394 /**
42395  * @class Roo.DDView
42396  * A DnD enabled version of Roo.View.
42397  * @param {Element/String} container The Element in which to create the View.
42398  * @param {String} tpl The template string used to create the markup for each element of the View
42399  * @param {Object} config The configuration properties. These include all the config options of
42400  * {@link Roo.View} plus some specific to this class.<br>
42401  * <p>
42402  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42403  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42404  * <p>
42405  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42406 .x-view-drag-insert-above {
42407         border-top:1px dotted #3366cc;
42408 }
42409 .x-view-drag-insert-below {
42410         border-bottom:1px dotted #3366cc;
42411 }
42412 </code></pre>
42413  * 
42414  */
42415  
42416 Roo.DDView = function(container, tpl, config) {
42417     Roo.DDView.superclass.constructor.apply(this, arguments);
42418     this.getEl().setStyle("outline", "0px none");
42419     this.getEl().unselectable();
42420     if (this.dragGroup) {
42421                 this.setDraggable(this.dragGroup.split(","));
42422     }
42423     if (this.dropGroup) {
42424                 this.setDroppable(this.dropGroup.split(","));
42425     }
42426     if (this.deletable) {
42427         this.setDeletable();
42428     }
42429     this.isDirtyFlag = false;
42430         this.addEvents({
42431                 "drop" : true
42432         });
42433 };
42434
42435 Roo.extend(Roo.DDView, Roo.View, {
42436 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42437 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42438 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42439 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42440
42441         isFormField: true,
42442
42443         reset: Roo.emptyFn,
42444         
42445         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42446
42447         validate: function() {
42448                 return true;
42449         },
42450         
42451         destroy: function() {
42452                 this.purgeListeners();
42453                 this.getEl.removeAllListeners();
42454                 this.getEl().remove();
42455                 if (this.dragZone) {
42456                         if (this.dragZone.destroy) {
42457                                 this.dragZone.destroy();
42458                         }
42459                 }
42460                 if (this.dropZone) {
42461                         if (this.dropZone.destroy) {
42462                                 this.dropZone.destroy();
42463                         }
42464                 }
42465         },
42466
42467 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42468         getName: function() {
42469                 return this.name;
42470         },
42471
42472 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42473         setValue: function(v) {
42474                 if (!this.store) {
42475                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42476                 }
42477                 var data = {};
42478                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42479                 this.store.proxy = new Roo.data.MemoryProxy(data);
42480                 this.store.load();
42481         },
42482
42483 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42484         getValue: function() {
42485                 var result = '(';
42486                 this.store.each(function(rec) {
42487                         result += rec.id + ',';
42488                 });
42489                 return result.substr(0, result.length - 1) + ')';
42490         },
42491         
42492         getIds: function() {
42493                 var i = 0, result = new Array(this.store.getCount());
42494                 this.store.each(function(rec) {
42495                         result[i++] = rec.id;
42496                 });
42497                 return result;
42498         },
42499         
42500         isDirty: function() {
42501                 return this.isDirtyFlag;
42502         },
42503
42504 /**
42505  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
42506  *      whole Element becomes the target, and this causes the drop gesture to append.
42507  */
42508     getTargetFromEvent : function(e) {
42509                 var target = e.getTarget();
42510                 while ((target !== null) && (target.parentNode != this.el.dom)) {
42511                 target = target.parentNode;
42512                 }
42513                 if (!target) {
42514                         target = this.el.dom.lastChild || this.el.dom;
42515                 }
42516                 return target;
42517     },
42518
42519 /**
42520  *      Create the drag data which consists of an object which has the property "ddel" as
42521  *      the drag proxy element. 
42522  */
42523     getDragData : function(e) {
42524         var target = this.findItemFromChild(e.getTarget());
42525                 if(target) {
42526                         this.handleSelection(e);
42527                         var selNodes = this.getSelectedNodes();
42528             var dragData = {
42529                 source: this,
42530                 copy: this.copy || (this.allowCopy && e.ctrlKey),
42531                 nodes: selNodes,
42532                 records: []
42533                         };
42534                         var selectedIndices = this.getSelectedIndexes();
42535                         for (var i = 0; i < selectedIndices.length; i++) {
42536                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
42537                         }
42538                         if (selNodes.length == 1) {
42539                                 dragData.ddel = target.cloneNode(true); // the div element
42540                         } else {
42541                                 var div = document.createElement('div'); // create the multi element drag "ghost"
42542                                 div.className = 'multi-proxy';
42543                                 for (var i = 0, len = selNodes.length; i < len; i++) {
42544                                         div.appendChild(selNodes[i].cloneNode(true));
42545                                 }
42546                                 dragData.ddel = div;
42547                         }
42548             //console.log(dragData)
42549             //console.log(dragData.ddel.innerHTML)
42550                         return dragData;
42551                 }
42552         //console.log('nodragData')
42553                 return false;
42554     },
42555     
42556 /**     Specify to which ddGroup items in this DDView may be dragged. */
42557     setDraggable: function(ddGroup) {
42558         if (ddGroup instanceof Array) {
42559                 Roo.each(ddGroup, this.setDraggable, this);
42560                 return;
42561         }
42562         if (this.dragZone) {
42563                 this.dragZone.addToGroup(ddGroup);
42564         } else {
42565                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
42566                                 containerScroll: true,
42567                                 ddGroup: ddGroup 
42568
42569                         });
42570 //                      Draggability implies selection. DragZone's mousedown selects the element.
42571                         if (!this.multiSelect) { this.singleSelect = true; }
42572
42573 //                      Wire the DragZone's handlers up to methods in *this*
42574                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
42575                 }
42576     },
42577
42578 /**     Specify from which ddGroup this DDView accepts drops. */
42579     setDroppable: function(ddGroup) {
42580         if (ddGroup instanceof Array) {
42581                 Roo.each(ddGroup, this.setDroppable, this);
42582                 return;
42583         }
42584         if (this.dropZone) {
42585                 this.dropZone.addToGroup(ddGroup);
42586         } else {
42587                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
42588                                 containerScroll: true,
42589                                 ddGroup: ddGroup
42590                         });
42591
42592 //                      Wire the DropZone's handlers up to methods in *this*
42593                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
42594                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
42595                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
42596                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
42597                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
42598                 }
42599     },
42600
42601 /**     Decide whether to drop above or below a View node. */
42602     getDropPoint : function(e, n, dd){
42603         if (n == this.el.dom) { return "above"; }
42604                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42605                 var c = t + (b - t) / 2;
42606                 var y = Roo.lib.Event.getPageY(e);
42607                 if(y <= c) {
42608                         return "above";
42609                 }else{
42610                         return "below";
42611                 }
42612     },
42613
42614     onNodeEnter : function(n, dd, e, data){
42615                 return false;
42616     },
42617     
42618     onNodeOver : function(n, dd, e, data){
42619                 var pt = this.getDropPoint(e, n, dd);
42620                 // set the insert point style on the target node
42621                 var dragElClass = this.dropNotAllowed;
42622                 if (pt) {
42623                         var targetElClass;
42624                         if (pt == "above"){
42625                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42626                                 targetElClass = "x-view-drag-insert-above";
42627                         } else {
42628                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42629                                 targetElClass = "x-view-drag-insert-below";
42630                         }
42631                         if (this.lastInsertClass != targetElClass){
42632                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42633                                 this.lastInsertClass = targetElClass;
42634                         }
42635                 }
42636                 return dragElClass;
42637         },
42638
42639     onNodeOut : function(n, dd, e, data){
42640                 this.removeDropIndicators(n);
42641     },
42642
42643     onNodeDrop : function(n, dd, e, data){
42644         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42645                 return false;
42646         }
42647         var pt = this.getDropPoint(e, n, dd);
42648                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42649                 if (pt == "below") { insertAt++; }
42650                 for (var i = 0; i < data.records.length; i++) {
42651                         var r = data.records[i];
42652                         var dup = this.store.getById(r.id);
42653                         if (dup && (dd != this.dragZone)) {
42654                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42655                         } else {
42656                                 if (data.copy) {
42657                                         this.store.insert(insertAt++, r.copy());
42658                                 } else {
42659                                         data.source.isDirtyFlag = true;
42660                                         r.store.remove(r);
42661                                         this.store.insert(insertAt++, r);
42662                                 }
42663                                 this.isDirtyFlag = true;
42664                         }
42665                 }
42666                 this.dragZone.cachedTarget = null;
42667                 return true;
42668     },
42669
42670     removeDropIndicators : function(n){
42671                 if(n){
42672                         Roo.fly(n).removeClass([
42673                                 "x-view-drag-insert-above",
42674                                 "x-view-drag-insert-below"]);
42675                         this.lastInsertClass = "_noclass";
42676                 }
42677     },
42678
42679 /**
42680  *      Utility method. Add a delete option to the DDView's context menu.
42681  *      @param {String} imageUrl The URL of the "delete" icon image.
42682  */
42683         setDeletable: function(imageUrl) {
42684                 if (!this.singleSelect && !this.multiSelect) {
42685                         this.singleSelect = true;
42686                 }
42687                 var c = this.getContextMenu();
42688                 this.contextMenu.on("itemclick", function(item) {
42689                         switch (item.id) {
42690                                 case "delete":
42691                                         this.remove(this.getSelectedIndexes());
42692                                         break;
42693                         }
42694                 }, this);
42695                 this.contextMenu.add({
42696                         icon: imageUrl,
42697                         id: "delete",
42698                         text: 'Delete'
42699                 });
42700         },
42701         
42702 /**     Return the context menu for this DDView. */
42703         getContextMenu: function() {
42704                 if (!this.contextMenu) {
42705 //                      Create the View's context menu
42706                         this.contextMenu = new Roo.menu.Menu({
42707                                 id: this.id + "-contextmenu"
42708                         });
42709                         this.el.on("contextmenu", this.showContextMenu, this);
42710                 }
42711                 return this.contextMenu;
42712         },
42713         
42714         disableContextMenu: function() {
42715                 if (this.contextMenu) {
42716                         this.el.un("contextmenu", this.showContextMenu, this);
42717                 }
42718         },
42719
42720         showContextMenu: function(e, item) {
42721         item = this.findItemFromChild(e.getTarget());
42722                 if (item) {
42723                         e.stopEvent();
42724                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42725                         this.contextMenu.showAt(e.getXY());
42726             }
42727     },
42728
42729 /**
42730  *      Remove {@link Roo.data.Record}s at the specified indices.
42731  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42732  */
42733     remove: function(selectedIndices) {
42734                 selectedIndices = [].concat(selectedIndices);
42735                 for (var i = 0; i < selectedIndices.length; i++) {
42736                         var rec = this.store.getAt(selectedIndices[i]);
42737                         this.store.remove(rec);
42738                 }
42739     },
42740
42741 /**
42742  *      Double click fires the event, but also, if this is draggable, and there is only one other
42743  *      related DropZone, it transfers the selected node.
42744  */
42745     onDblClick : function(e){
42746         var item = this.findItemFromChild(e.getTarget());
42747         if(item){
42748             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42749                 return false;
42750             }
42751             if (this.dragGroup) {
42752                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42753                     while (targets.indexOf(this.dropZone) > -1) {
42754                             targets.remove(this.dropZone);
42755                                 }
42756                     if (targets.length == 1) {
42757                                         this.dragZone.cachedTarget = null;
42758                         var el = Roo.get(targets[0].getEl());
42759                         var box = el.getBox(true);
42760                         targets[0].onNodeDrop(el.dom, {
42761                                 target: el.dom,
42762                                 xy: [box.x, box.y + box.height - 1]
42763                         }, null, this.getDragData(e));
42764                     }
42765                 }
42766         }
42767     },
42768     
42769     handleSelection: function(e) {
42770                 this.dragZone.cachedTarget = null;
42771         var item = this.findItemFromChild(e.getTarget());
42772         if (!item) {
42773                 this.clearSelections(true);
42774                 return;
42775         }
42776                 if (item && (this.multiSelect || this.singleSelect)){
42777                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42778                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42779                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42780                                 this.unselect(item);
42781                         } else {
42782                                 this.select(item, this.multiSelect && e.ctrlKey);
42783                                 this.lastSelection = item;
42784                         }
42785                 }
42786     },
42787
42788     onItemClick : function(item, index, e){
42789                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
42790                         return false;
42791                 }
42792                 return true;
42793     },
42794
42795     unselect : function(nodeInfo, suppressEvent){
42796                 var node = this.getNode(nodeInfo);
42797                 if(node && this.isSelected(node)){
42798                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
42799                                 Roo.fly(node).removeClass(this.selectedClass);
42800                                 this.selections.remove(node);
42801                                 if(!suppressEvent){
42802                                         this.fireEvent("selectionchange", this, this.selections);
42803                                 }
42804                         }
42805                 }
42806     }
42807 });
42808 /*
42809  * Based on:
42810  * Ext JS Library 1.1.1
42811  * Copyright(c) 2006-2007, Ext JS, LLC.
42812  *
42813  * Originally Released Under LGPL - original licence link has changed is not relivant.
42814  *
42815  * Fork - LGPL
42816  * <script type="text/javascript">
42817  */
42818  
42819 /**
42820  * @class Roo.LayoutManager
42821  * @extends Roo.util.Observable
42822  * Base class for layout managers.
42823  */
42824 Roo.LayoutManager = function(container, config){
42825     Roo.LayoutManager.superclass.constructor.call(this);
42826     this.el = Roo.get(container);
42827     // ie scrollbar fix
42828     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42829         document.body.scroll = "no";
42830     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42831         this.el.position('relative');
42832     }
42833     this.id = this.el.id;
42834     this.el.addClass("x-layout-container");
42835     /** false to disable window resize monitoring @type Boolean */
42836     this.monitorWindowResize = true;
42837     this.regions = {};
42838     this.addEvents({
42839         /**
42840          * @event layout
42841          * Fires when a layout is performed. 
42842          * @param {Roo.LayoutManager} this
42843          */
42844         "layout" : true,
42845         /**
42846          * @event regionresized
42847          * Fires when the user resizes a region. 
42848          * @param {Roo.LayoutRegion} region The resized region
42849          * @param {Number} newSize The new size (width for east/west, height for north/south)
42850          */
42851         "regionresized" : true,
42852         /**
42853          * @event regioncollapsed
42854          * Fires when a region is collapsed. 
42855          * @param {Roo.LayoutRegion} region The collapsed region
42856          */
42857         "regioncollapsed" : true,
42858         /**
42859          * @event regionexpanded
42860          * Fires when a region is expanded.  
42861          * @param {Roo.LayoutRegion} region The expanded region
42862          */
42863         "regionexpanded" : true
42864     });
42865     this.updating = false;
42866     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42867 };
42868
42869 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
42870     /**
42871      * Returns true if this layout is currently being updated
42872      * @return {Boolean}
42873      */
42874     isUpdating : function(){
42875         return this.updating; 
42876     },
42877     
42878     /**
42879      * Suspend the LayoutManager from doing auto-layouts while
42880      * making multiple add or remove calls
42881      */
42882     beginUpdate : function(){
42883         this.updating = true;    
42884     },
42885     
42886     /**
42887      * Restore auto-layouts and optionally disable the manager from performing a layout
42888      * @param {Boolean} noLayout true to disable a layout update 
42889      */
42890     endUpdate : function(noLayout){
42891         this.updating = false;
42892         if(!noLayout){
42893             this.layout();
42894         }    
42895     },
42896     
42897     layout: function(){
42898         
42899     },
42900     
42901     onRegionResized : function(region, newSize){
42902         this.fireEvent("regionresized", region, newSize);
42903         this.layout();
42904     },
42905     
42906     onRegionCollapsed : function(region){
42907         this.fireEvent("regioncollapsed", region);
42908     },
42909     
42910     onRegionExpanded : function(region){
42911         this.fireEvent("regionexpanded", region);
42912     },
42913         
42914     /**
42915      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42916      * performs box-model adjustments.
42917      * @return {Object} The size as an object {width: (the width), height: (the height)}
42918      */
42919     getViewSize : function(){
42920         var size;
42921         if(this.el.dom != document.body){
42922             size = this.el.getSize();
42923         }else{
42924             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42925         }
42926         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42927         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42928         return size;
42929     },
42930     
42931     /**
42932      * Returns the Element this layout is bound to.
42933      * @return {Roo.Element}
42934      */
42935     getEl : function(){
42936         return this.el;
42937     },
42938     
42939     /**
42940      * Returns the specified region.
42941      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42942      * @return {Roo.LayoutRegion}
42943      */
42944     getRegion : function(target){
42945         return this.regions[target.toLowerCase()];
42946     },
42947     
42948     onWindowResize : function(){
42949         if(this.monitorWindowResize){
42950             this.layout();
42951         }
42952     }
42953 });/*
42954  * Based on:
42955  * Ext JS Library 1.1.1
42956  * Copyright(c) 2006-2007, Ext JS, LLC.
42957  *
42958  * Originally Released Under LGPL - original licence link has changed is not relivant.
42959  *
42960  * Fork - LGPL
42961  * <script type="text/javascript">
42962  */
42963 /**
42964  * @class Roo.BorderLayout
42965  * @extends Roo.LayoutManager
42966  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42967  * please see: <br><br>
42968  * <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>
42969  * <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>
42970  * Example:
42971  <pre><code>
42972  var layout = new Roo.BorderLayout(document.body, {
42973     north: {
42974         initialSize: 25,
42975         titlebar: false
42976     },
42977     west: {
42978         split:true,
42979         initialSize: 200,
42980         minSize: 175,
42981         maxSize: 400,
42982         titlebar: true,
42983         collapsible: true
42984     },
42985     east: {
42986         split:true,
42987         initialSize: 202,
42988         minSize: 175,
42989         maxSize: 400,
42990         titlebar: true,
42991         collapsible: true
42992     },
42993     south: {
42994         split:true,
42995         initialSize: 100,
42996         minSize: 100,
42997         maxSize: 200,
42998         titlebar: true,
42999         collapsible: true
43000     },
43001     center: {
43002         titlebar: true,
43003         autoScroll:true,
43004         resizeTabs: true,
43005         minTabWidth: 50,
43006         preferredTabWidth: 150
43007     }
43008 });
43009
43010 // shorthand
43011 var CP = Roo.ContentPanel;
43012
43013 layout.beginUpdate();
43014 layout.add("north", new CP("north", "North"));
43015 layout.add("south", new CP("south", {title: "South", closable: true}));
43016 layout.add("west", new CP("west", {title: "West"}));
43017 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43018 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43019 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43020 layout.getRegion("center").showPanel("center1");
43021 layout.endUpdate();
43022 </code></pre>
43023
43024 <b>The container the layout is rendered into can be either the body element or any other element.
43025 If it is not the body element, the container needs to either be an absolute positioned element,
43026 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43027 the container size if it is not the body element.</b>
43028
43029 * @constructor
43030 * Create a new BorderLayout
43031 * @param {String/HTMLElement/Element} container The container this layout is bound to
43032 * @param {Object} config Configuration options
43033  */
43034 Roo.BorderLayout = function(container, config){
43035     config = config || {};
43036     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43037     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43038     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43039         var target = this.factory.validRegions[i];
43040         if(config[target]){
43041             this.addRegion(target, config[target]);
43042         }
43043     }
43044 };
43045
43046 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43047     /**
43048      * Creates and adds a new region if it doesn't already exist.
43049      * @param {String} target The target region key (north, south, east, west or center).
43050      * @param {Object} config The regions config object
43051      * @return {BorderLayoutRegion} The new region
43052      */
43053     addRegion : function(target, config){
43054         if(!this.regions[target]){
43055             var r = this.factory.create(target, this, config);
43056             this.bindRegion(target, r);
43057         }
43058         return this.regions[target];
43059     },
43060
43061     // private (kinda)
43062     bindRegion : function(name, r){
43063         this.regions[name] = r;
43064         r.on("visibilitychange", this.layout, this);
43065         r.on("paneladded", this.layout, this);
43066         r.on("panelremoved", this.layout, this);
43067         r.on("invalidated", this.layout, this);
43068         r.on("resized", this.onRegionResized, this);
43069         r.on("collapsed", this.onRegionCollapsed, this);
43070         r.on("expanded", this.onRegionExpanded, this);
43071     },
43072
43073     /**
43074      * Performs a layout update.
43075      */
43076     layout : function(){
43077         if(this.updating) return;
43078         var size = this.getViewSize();
43079         var w = size.width;
43080         var h = size.height;
43081         var centerW = w;
43082         var centerH = h;
43083         var centerY = 0;
43084         var centerX = 0;
43085         //var x = 0, y = 0;
43086
43087         var rs = this.regions;
43088         var north = rs["north"];
43089         var south = rs["south"]; 
43090         var west = rs["west"];
43091         var east = rs["east"];
43092         var center = rs["center"];
43093         //if(this.hideOnLayout){ // not supported anymore
43094             //c.el.setStyle("display", "none");
43095         //}
43096         if(north && north.isVisible()){
43097             var b = north.getBox();
43098             var m = north.getMargins();
43099             b.width = w - (m.left+m.right);
43100             b.x = m.left;
43101             b.y = m.top;
43102             centerY = b.height + b.y + m.bottom;
43103             centerH -= centerY;
43104             north.updateBox(this.safeBox(b));
43105         }
43106         if(south && south.isVisible()){
43107             var b = south.getBox();
43108             var m = south.getMargins();
43109             b.width = w - (m.left+m.right);
43110             b.x = m.left;
43111             var totalHeight = (b.height + m.top + m.bottom);
43112             b.y = h - totalHeight + m.top;
43113             centerH -= totalHeight;
43114             south.updateBox(this.safeBox(b));
43115         }
43116         if(west && west.isVisible()){
43117             var b = west.getBox();
43118             var m = west.getMargins();
43119             b.height = centerH - (m.top+m.bottom);
43120             b.x = m.left;
43121             b.y = centerY + m.top;
43122             var totalWidth = (b.width + m.left + m.right);
43123             centerX += totalWidth;
43124             centerW -= totalWidth;
43125             west.updateBox(this.safeBox(b));
43126         }
43127         if(east && east.isVisible()){
43128             var b = east.getBox();
43129             var m = east.getMargins();
43130             b.height = centerH - (m.top+m.bottom);
43131             var totalWidth = (b.width + m.left + m.right);
43132             b.x = w - totalWidth + m.left;
43133             b.y = centerY + m.top;
43134             centerW -= totalWidth;
43135             east.updateBox(this.safeBox(b));
43136         }
43137         if(center){
43138             var m = center.getMargins();
43139             var centerBox = {
43140                 x: centerX + m.left,
43141                 y: centerY + m.top,
43142                 width: centerW - (m.left+m.right),
43143                 height: centerH - (m.top+m.bottom)
43144             };
43145             //if(this.hideOnLayout){
43146                 //center.el.setStyle("display", "block");
43147             //}
43148             center.updateBox(this.safeBox(centerBox));
43149         }
43150         this.el.repaint();
43151         this.fireEvent("layout", this);
43152     },
43153
43154     // private
43155     safeBox : function(box){
43156         box.width = Math.max(0, box.width);
43157         box.height = Math.max(0, box.height);
43158         return box;
43159     },
43160
43161     /**
43162      * Adds a ContentPanel (or subclass) to this layout.
43163      * @param {String} target The target region key (north, south, east, west or center).
43164      * @param {Roo.ContentPanel} panel The panel to add
43165      * @return {Roo.ContentPanel} The added panel
43166      */
43167     add : function(target, panel){
43168          
43169         target = target.toLowerCase();
43170         return this.regions[target].add(panel);
43171     },
43172
43173     /**
43174      * Remove a ContentPanel (or subclass) to this layout.
43175      * @param {String} target The target region key (north, south, east, west or center).
43176      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43177      * @return {Roo.ContentPanel} The removed panel
43178      */
43179     remove : function(target, panel){
43180         target = target.toLowerCase();
43181         return this.regions[target].remove(panel);
43182     },
43183
43184     /**
43185      * Searches all regions for a panel with the specified id
43186      * @param {String} panelId
43187      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43188      */
43189     findPanel : function(panelId){
43190         var rs = this.regions;
43191         for(var target in rs){
43192             if(typeof rs[target] != "function"){
43193                 var p = rs[target].getPanel(panelId);
43194                 if(p){
43195                     return p;
43196                 }
43197             }
43198         }
43199         return null;
43200     },
43201
43202     /**
43203      * Searches all regions for a panel with the specified id and activates (shows) it.
43204      * @param {String/ContentPanel} panelId The panels id or the panel itself
43205      * @return {Roo.ContentPanel} The shown panel or null
43206      */
43207     showPanel : function(panelId) {
43208       var rs = this.regions;
43209       for(var target in rs){
43210          var r = rs[target];
43211          if(typeof r != "function"){
43212             if(r.hasPanel(panelId)){
43213                return r.showPanel(panelId);
43214             }
43215          }
43216       }
43217       return null;
43218    },
43219
43220    /**
43221      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43222      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43223      */
43224     restoreState : function(provider){
43225         if(!provider){
43226             provider = Roo.state.Manager;
43227         }
43228         var sm = new Roo.LayoutStateManager();
43229         sm.init(this, provider);
43230     },
43231
43232     /**
43233      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43234      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43235      * a valid ContentPanel config object.  Example:
43236      * <pre><code>
43237 // Create the main layout
43238 var layout = new Roo.BorderLayout('main-ct', {
43239     west: {
43240         split:true,
43241         minSize: 175,
43242         titlebar: true
43243     },
43244     center: {
43245         title:'Components'
43246     }
43247 }, 'main-ct');
43248
43249 // Create and add multiple ContentPanels at once via configs
43250 layout.batchAdd({
43251    west: {
43252        id: 'source-files',
43253        autoCreate:true,
43254        title:'Ext Source Files',
43255        autoScroll:true,
43256        fitToFrame:true
43257    },
43258    center : {
43259        el: cview,
43260        autoScroll:true,
43261        fitToFrame:true,
43262        toolbar: tb,
43263        resizeEl:'cbody'
43264    }
43265 });
43266 </code></pre>
43267      * @param {Object} regions An object containing ContentPanel configs by region name
43268      */
43269     batchAdd : function(regions){
43270         this.beginUpdate();
43271         for(var rname in regions){
43272             var lr = this.regions[rname];
43273             if(lr){
43274                 this.addTypedPanels(lr, regions[rname]);
43275             }
43276         }
43277         this.endUpdate();
43278     },
43279
43280     // private
43281     addTypedPanels : function(lr, ps){
43282         if(typeof ps == 'string'){
43283             lr.add(new Roo.ContentPanel(ps));
43284         }
43285         else if(ps instanceof Array){
43286             for(var i =0, len = ps.length; i < len; i++){
43287                 this.addTypedPanels(lr, ps[i]);
43288             }
43289         }
43290         else if(!ps.events){ // raw config?
43291             var el = ps.el;
43292             delete ps.el; // prevent conflict
43293             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43294         }
43295         else {  // panel object assumed!
43296             lr.add(ps);
43297         }
43298     },
43299     /**
43300      * Adds a xtype elements to the layout.
43301      * <pre><code>
43302
43303 layout.addxtype({
43304        xtype : 'ContentPanel',
43305        region: 'west',
43306        items: [ .... ]
43307    }
43308 );
43309
43310 layout.addxtype({
43311         xtype : 'NestedLayoutPanel',
43312         region: 'west',
43313         layout: {
43314            center: { },
43315            west: { }   
43316         },
43317         items : [ ... list of content panels or nested layout panels.. ]
43318    }
43319 );
43320 </code></pre>
43321      * @param {Object} cfg Xtype definition of item to add.
43322      */
43323     addxtype : function(cfg)
43324     {
43325         // basically accepts a pannel...
43326         // can accept a layout region..!?!?
43327        // console.log('BorderLayout add ' + cfg.xtype)
43328         
43329         if (!cfg.xtype.match(/Panel$/)) {
43330             return false;
43331         }
43332         var ret = false;
43333         var region = cfg.region;
43334         delete cfg.region;
43335         
43336           
43337         var xitems = [];
43338         if (cfg.items) {
43339             xitems = cfg.items;
43340             delete cfg.items;
43341         }
43342         
43343         
43344         switch(cfg.xtype) 
43345         {
43346             case 'ContentPanel':  // ContentPanel (el, cfg)
43347             case 'ScrollPanel':  // ContentPanel (el, cfg)
43348                 if(cfg.autoCreate) {
43349                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43350                 } else {
43351                     var el = this.el.createChild();
43352                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43353                 }
43354                 
43355                 this.add(region, ret);
43356                 break;
43357             
43358             
43359             case 'TreePanel': // our new panel!
43360                 cfg.el = this.el.createChild();
43361                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43362                 this.add(region, ret);
43363                 break;
43364             
43365             case 'NestedLayoutPanel': 
43366                 // create a new Layout (which is  a Border Layout...
43367                 var el = this.el.createChild();
43368                 var clayout = cfg.layout;
43369                 delete cfg.layout;
43370                 clayout.items   = clayout.items  || [];
43371                 // replace this exitems with the clayout ones..
43372                 xitems = clayout.items;
43373                  
43374                 
43375                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43376                     cfg.background = false;
43377                 }
43378                 var layout = new Roo.BorderLayout(el, clayout);
43379                 
43380                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43381                 //console.log('adding nested layout panel '  + cfg.toSource());
43382                 this.add(region, ret);
43383                 
43384                 break;
43385                 
43386             case 'GridPanel': 
43387             
43388                 // needs grid and region
43389                 
43390                 //var el = this.getRegion(region).el.createChild();
43391                 var el = this.el.createChild();
43392                 // create the grid first...
43393                 
43394                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43395                 delete cfg.grid;
43396                 if (region == 'center' && this.active ) {
43397                     cfg.background = false;
43398                 }
43399                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43400                 
43401                 this.add(region, ret);
43402                 if (cfg.background) {
43403                     ret.on('activate', function(gp) {
43404                         if (!gp.grid.rendered) {
43405                             gp.grid.render();
43406                         }
43407                     });
43408                 } else {
43409                     grid.render();
43410                 }
43411                 break;
43412            
43413                
43414                 
43415                 
43416             default: 
43417                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43418                 return;
43419              // GridPanel (grid, cfg)
43420             
43421         }
43422         this.beginUpdate();
43423         // add children..
43424         Roo.each(xitems, function(i)  {
43425             ret.addxtype(i);
43426         });
43427         this.endUpdate();
43428         return ret;
43429         
43430     }
43431 });
43432
43433 /**
43434  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43435  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43436  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43437  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43438  * <pre><code>
43439 // shorthand
43440 var CP = Roo.ContentPanel;
43441
43442 var layout = Roo.BorderLayout.create({
43443     north: {
43444         initialSize: 25,
43445         titlebar: false,
43446         panels: [new CP("north", "North")]
43447     },
43448     west: {
43449         split:true,
43450         initialSize: 200,
43451         minSize: 175,
43452         maxSize: 400,
43453         titlebar: true,
43454         collapsible: true,
43455         panels: [new CP("west", {title: "West"})]
43456     },
43457     east: {
43458         split:true,
43459         initialSize: 202,
43460         minSize: 175,
43461         maxSize: 400,
43462         titlebar: true,
43463         collapsible: true,
43464         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43465     },
43466     south: {
43467         split:true,
43468         initialSize: 100,
43469         minSize: 100,
43470         maxSize: 200,
43471         titlebar: true,
43472         collapsible: true,
43473         panels: [new CP("south", {title: "South", closable: true})]
43474     },
43475     center: {
43476         titlebar: true,
43477         autoScroll:true,
43478         resizeTabs: true,
43479         minTabWidth: 50,
43480         preferredTabWidth: 150,
43481         panels: [
43482             new CP("center1", {title: "Close Me", closable: true}),
43483             new CP("center2", {title: "Center Panel", closable: false})
43484         ]
43485     }
43486 }, document.body);
43487
43488 layout.getRegion("center").showPanel("center1");
43489 </code></pre>
43490  * @param config
43491  * @param targetEl
43492  */
43493 Roo.BorderLayout.create = function(config, targetEl){
43494     var layout = new Roo.BorderLayout(targetEl || document.body, config);
43495     layout.beginUpdate();
43496     var regions = Roo.BorderLayout.RegionFactory.validRegions;
43497     for(var j = 0, jlen = regions.length; j < jlen; j++){
43498         var lr = regions[j];
43499         if(layout.regions[lr] && config[lr].panels){
43500             var r = layout.regions[lr];
43501             var ps = config[lr].panels;
43502             layout.addTypedPanels(r, ps);
43503         }
43504     }
43505     layout.endUpdate();
43506     return layout;
43507 };
43508
43509 // private
43510 Roo.BorderLayout.RegionFactory = {
43511     // private
43512     validRegions : ["north","south","east","west","center"],
43513
43514     // private
43515     create : function(target, mgr, config){
43516         target = target.toLowerCase();
43517         if(config.lightweight || config.basic){
43518             return new Roo.BasicLayoutRegion(mgr, config, target);
43519         }
43520         switch(target){
43521             case "north":
43522                 return new Roo.NorthLayoutRegion(mgr, config);
43523             case "south":
43524                 return new Roo.SouthLayoutRegion(mgr, config);
43525             case "east":
43526                 return new Roo.EastLayoutRegion(mgr, config);
43527             case "west":
43528                 return new Roo.WestLayoutRegion(mgr, config);
43529             case "center":
43530                 return new Roo.CenterLayoutRegion(mgr, config);
43531         }
43532         throw 'Layout region "'+target+'" not supported.';
43533     }
43534 };/*
43535  * Based on:
43536  * Ext JS Library 1.1.1
43537  * Copyright(c) 2006-2007, Ext JS, LLC.
43538  *
43539  * Originally Released Under LGPL - original licence link has changed is not relivant.
43540  *
43541  * Fork - LGPL
43542  * <script type="text/javascript">
43543  */
43544  
43545 /**
43546  * @class Roo.BasicLayoutRegion
43547  * @extends Roo.util.Observable
43548  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43549  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43550  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43551  */
43552 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
43553     this.mgr = mgr;
43554     this.position  = pos;
43555     this.events = {
43556         /**
43557          * @scope Roo.BasicLayoutRegion
43558          */
43559         
43560         /**
43561          * @event beforeremove
43562          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43563          * @param {Roo.LayoutRegion} this
43564          * @param {Roo.ContentPanel} panel The panel
43565          * @param {Object} e The cancel event object
43566          */
43567         "beforeremove" : true,
43568         /**
43569          * @event invalidated
43570          * Fires when the layout for this region is changed.
43571          * @param {Roo.LayoutRegion} this
43572          */
43573         "invalidated" : true,
43574         /**
43575          * @event visibilitychange
43576          * Fires when this region is shown or hidden 
43577          * @param {Roo.LayoutRegion} this
43578          * @param {Boolean} visibility true or false
43579          */
43580         "visibilitychange" : true,
43581         /**
43582          * @event paneladded
43583          * Fires when a panel is added. 
43584          * @param {Roo.LayoutRegion} this
43585          * @param {Roo.ContentPanel} panel The panel
43586          */
43587         "paneladded" : true,
43588         /**
43589          * @event panelremoved
43590          * Fires when a panel is removed. 
43591          * @param {Roo.LayoutRegion} this
43592          * @param {Roo.ContentPanel} panel The panel
43593          */
43594         "panelremoved" : true,
43595         /**
43596          * @event collapsed
43597          * Fires when this region is collapsed.
43598          * @param {Roo.LayoutRegion} this
43599          */
43600         "collapsed" : true,
43601         /**
43602          * @event expanded
43603          * Fires when this region is expanded.
43604          * @param {Roo.LayoutRegion} this
43605          */
43606         "expanded" : true,
43607         /**
43608          * @event slideshow
43609          * Fires when this region is slid into view.
43610          * @param {Roo.LayoutRegion} this
43611          */
43612         "slideshow" : true,
43613         /**
43614          * @event slidehide
43615          * Fires when this region slides out of view. 
43616          * @param {Roo.LayoutRegion} this
43617          */
43618         "slidehide" : true,
43619         /**
43620          * @event panelactivated
43621          * Fires when a panel is activated. 
43622          * @param {Roo.LayoutRegion} this
43623          * @param {Roo.ContentPanel} panel The activated panel
43624          */
43625         "panelactivated" : true,
43626         /**
43627          * @event resized
43628          * Fires when the user resizes this region. 
43629          * @param {Roo.LayoutRegion} this
43630          * @param {Number} newSize The new size (width for east/west, height for north/south)
43631          */
43632         "resized" : true
43633     };
43634     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43635     this.panels = new Roo.util.MixedCollection();
43636     this.panels.getKey = this.getPanelId.createDelegate(this);
43637     this.box = null;
43638     this.activePanel = null;
43639     // ensure listeners are added...
43640     
43641     if (config.listeners || config.events) {
43642         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43643             listeners : config.listeners || {},
43644             events : config.events || {}
43645         });
43646     }
43647     
43648     if(skipConfig !== true){
43649         this.applyConfig(config);
43650     }
43651 };
43652
43653 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43654     getPanelId : function(p){
43655         return p.getId();
43656     },
43657     
43658     applyConfig : function(config){
43659         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43660         this.config = config;
43661         
43662     },
43663     
43664     /**
43665      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43666      * the width, for horizontal (north, south) the height.
43667      * @param {Number} newSize The new width or height
43668      */
43669     resizeTo : function(newSize){
43670         var el = this.el ? this.el :
43671                  (this.activePanel ? this.activePanel.getEl() : null);
43672         if(el){
43673             switch(this.position){
43674                 case "east":
43675                 case "west":
43676                     el.setWidth(newSize);
43677                     this.fireEvent("resized", this, newSize);
43678                 break;
43679                 case "north":
43680                 case "south":
43681                     el.setHeight(newSize);
43682                     this.fireEvent("resized", this, newSize);
43683                 break;                
43684             }
43685         }
43686     },
43687     
43688     getBox : function(){
43689         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43690     },
43691     
43692     getMargins : function(){
43693         return this.margins;
43694     },
43695     
43696     updateBox : function(box){
43697         this.box = box;
43698         var el = this.activePanel.getEl();
43699         el.dom.style.left = box.x + "px";
43700         el.dom.style.top = box.y + "px";
43701         this.activePanel.setSize(box.width, box.height);
43702     },
43703     
43704     /**
43705      * Returns the container element for this region.
43706      * @return {Roo.Element}
43707      */
43708     getEl : function(){
43709         return this.activePanel;
43710     },
43711     
43712     /**
43713      * Returns true if this region is currently visible.
43714      * @return {Boolean}
43715      */
43716     isVisible : function(){
43717         return this.activePanel ? true : false;
43718     },
43719     
43720     setActivePanel : function(panel){
43721         panel = this.getPanel(panel);
43722         if(this.activePanel && this.activePanel != panel){
43723             this.activePanel.setActiveState(false);
43724             this.activePanel.getEl().setLeftTop(-10000,-10000);
43725         }
43726         this.activePanel = panel;
43727         panel.setActiveState(true);
43728         if(this.box){
43729             panel.setSize(this.box.width, this.box.height);
43730         }
43731         this.fireEvent("panelactivated", this, panel);
43732         this.fireEvent("invalidated");
43733     },
43734     
43735     /**
43736      * Show the specified panel.
43737      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43738      * @return {Roo.ContentPanel} The shown panel or null
43739      */
43740     showPanel : function(panel){
43741         if(panel = this.getPanel(panel)){
43742             this.setActivePanel(panel);
43743         }
43744         return panel;
43745     },
43746     
43747     /**
43748      * Get the active panel for this region.
43749      * @return {Roo.ContentPanel} The active panel or null
43750      */
43751     getActivePanel : function(){
43752         return this.activePanel;
43753     },
43754     
43755     /**
43756      * Add the passed ContentPanel(s)
43757      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43758      * @return {Roo.ContentPanel} The panel added (if only one was added)
43759      */
43760     add : function(panel){
43761         if(arguments.length > 1){
43762             for(var i = 0, len = arguments.length; i < len; i++) {
43763                 this.add(arguments[i]);
43764             }
43765             return null;
43766         }
43767         if(this.hasPanel(panel)){
43768             this.showPanel(panel);
43769             return panel;
43770         }
43771         var el = panel.getEl();
43772         if(el.dom.parentNode != this.mgr.el.dom){
43773             this.mgr.el.dom.appendChild(el.dom);
43774         }
43775         if(panel.setRegion){
43776             panel.setRegion(this);
43777         }
43778         this.panels.add(panel);
43779         el.setStyle("position", "absolute");
43780         if(!panel.background){
43781             this.setActivePanel(panel);
43782             if(this.config.initialSize && this.panels.getCount()==1){
43783                 this.resizeTo(this.config.initialSize);
43784             }
43785         }
43786         this.fireEvent("paneladded", this, panel);
43787         return panel;
43788     },
43789     
43790     /**
43791      * Returns true if the panel is in this region.
43792      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43793      * @return {Boolean}
43794      */
43795     hasPanel : function(panel){
43796         if(typeof panel == "object"){ // must be panel obj
43797             panel = panel.getId();
43798         }
43799         return this.getPanel(panel) ? true : false;
43800     },
43801     
43802     /**
43803      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43804      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43805      * @param {Boolean} preservePanel Overrides the config preservePanel option
43806      * @return {Roo.ContentPanel} The panel that was removed
43807      */
43808     remove : function(panel, preservePanel){
43809         panel = this.getPanel(panel);
43810         if(!panel){
43811             return null;
43812         }
43813         var e = {};
43814         this.fireEvent("beforeremove", this, panel, e);
43815         if(e.cancel === true){
43816             return null;
43817         }
43818         var panelId = panel.getId();
43819         this.panels.removeKey(panelId);
43820         return panel;
43821     },
43822     
43823     /**
43824      * Returns the panel specified or null if it's not in this region.
43825      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43826      * @return {Roo.ContentPanel}
43827      */
43828     getPanel : function(id){
43829         if(typeof id == "object"){ // must be panel obj
43830             return id;
43831         }
43832         return this.panels.get(id);
43833     },
43834     
43835     /**
43836      * Returns this regions position (north/south/east/west/center).
43837      * @return {String} 
43838      */
43839     getPosition: function(){
43840         return this.position;    
43841     }
43842 });/*
43843  * Based on:
43844  * Ext JS Library 1.1.1
43845  * Copyright(c) 2006-2007, Ext JS, LLC.
43846  *
43847  * Originally Released Under LGPL - original licence link has changed is not relivant.
43848  *
43849  * Fork - LGPL
43850  * <script type="text/javascript">
43851  */
43852  
43853 /**
43854  * @class Roo.LayoutRegion
43855  * @extends Roo.BasicLayoutRegion
43856  * This class represents a region in a layout manager.
43857  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
43858  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
43859  * @cfg {Boolean} floatable False to disable floating (defaults to true)
43860  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43861  * @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})
43862  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
43863  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
43864  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43865  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43866  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43867  * @cfg {String} title The title for the region (overrides panel titles)
43868  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43869  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43870  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43871  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43872  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43873  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43874  * the space available, similar to FireFox 1.5 tabs (defaults to false)
43875  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43876  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43877  * @cfg {Boolean} showPin True to show a pin button
43878 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43879 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43880 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43881 * @cfg {Number} width  For East/West panels
43882 * @cfg {Number} height For North/South panels
43883 * @cfg {Boolean} split To show the splitter
43884  */
43885 Roo.LayoutRegion = function(mgr, config, pos){
43886     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
43887     var dh = Roo.DomHelper;
43888     /** This region's container element 
43889     * @type Roo.Element */
43890     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
43891     /** This region's title element 
43892     * @type Roo.Element */
43893
43894     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
43895         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43896         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
43897     ]}, true);
43898     this.titleEl.enableDisplayMode();
43899     /** This region's title text element 
43900     * @type HTMLElement */
43901     this.titleTextEl = this.titleEl.dom.firstChild;
43902     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43903     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43904     this.closeBtn.enableDisplayMode();
43905     this.closeBtn.on("click", this.closeClicked, this);
43906     this.closeBtn.hide();
43907
43908     this.createBody(config);
43909     this.visible = true;
43910     this.collapsed = false;
43911
43912     if(config.hideWhenEmpty){
43913         this.hide();
43914         this.on("paneladded", this.validateVisibility, this);
43915         this.on("panelremoved", this.validateVisibility, this);
43916     }
43917     this.applyConfig(config);
43918 };
43919
43920 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43921
43922     createBody : function(){
43923         /** This region's body element 
43924         * @type Roo.Element */
43925         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43926     },
43927
43928     applyConfig : function(c){
43929         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43930             var dh = Roo.DomHelper;
43931             if(c.titlebar !== false){
43932                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43933                 this.collapseBtn.on("click", this.collapse, this);
43934                 this.collapseBtn.enableDisplayMode();
43935
43936                 if(c.showPin === true || this.showPin){
43937                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43938                     this.stickBtn.enableDisplayMode();
43939                     this.stickBtn.on("click", this.expand, this);
43940                     this.stickBtn.hide();
43941                 }
43942             }
43943             /** This region's collapsed element
43944             * @type Roo.Element */
43945             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43946                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43947             ]}, true);
43948             if(c.floatable !== false){
43949                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43950                this.collapsedEl.on("click", this.collapseClick, this);
43951             }
43952
43953             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43954                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43955                    id: "message", unselectable: "on", style:{"float":"left"}});
43956                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43957              }
43958             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43959             this.expandBtn.on("click", this.expand, this);
43960         }
43961         if(this.collapseBtn){
43962             this.collapseBtn.setVisible(c.collapsible == true);
43963         }
43964         this.cmargins = c.cmargins || this.cmargins ||
43965                          (this.position == "west" || this.position == "east" ?
43966                              {top: 0, left: 2, right:2, bottom: 0} :
43967                              {top: 2, left: 0, right:0, bottom: 2});
43968         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43969         this.bottomTabs = c.tabPosition != "top";
43970         this.autoScroll = c.autoScroll || false;
43971         if(this.autoScroll){
43972             this.bodyEl.setStyle("overflow", "auto");
43973         }else{
43974             this.bodyEl.setStyle("overflow", "hidden");
43975         }
43976         //if(c.titlebar !== false){
43977             if((!c.titlebar && !c.title) || c.titlebar === false){
43978                 this.titleEl.hide();
43979             }else{
43980                 this.titleEl.show();
43981                 if(c.title){
43982                     this.titleTextEl.innerHTML = c.title;
43983                 }
43984             }
43985         //}
43986         this.duration = c.duration || .30;
43987         this.slideDuration = c.slideDuration || .45;
43988         this.config = c;
43989         if(c.collapsed){
43990             this.collapse(true);
43991         }
43992         if(c.hidden){
43993             this.hide();
43994         }
43995     },
43996     /**
43997      * Returns true if this region is currently visible.
43998      * @return {Boolean}
43999      */
44000     isVisible : function(){
44001         return this.visible;
44002     },
44003
44004     /**
44005      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44006      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44007      */
44008     setCollapsedTitle : function(title){
44009         title = title || "&#160;";
44010         if(this.collapsedTitleTextEl){
44011             this.collapsedTitleTextEl.innerHTML = title;
44012         }
44013     },
44014
44015     getBox : function(){
44016         var b;
44017         if(!this.collapsed){
44018             b = this.el.getBox(false, true);
44019         }else{
44020             b = this.collapsedEl.getBox(false, true);
44021         }
44022         return b;
44023     },
44024
44025     getMargins : function(){
44026         return this.collapsed ? this.cmargins : this.margins;
44027     },
44028
44029     highlight : function(){
44030         this.el.addClass("x-layout-panel-dragover");
44031     },
44032
44033     unhighlight : function(){
44034         this.el.removeClass("x-layout-panel-dragover");
44035     },
44036
44037     updateBox : function(box){
44038         this.box = box;
44039         if(!this.collapsed){
44040             this.el.dom.style.left = box.x + "px";
44041             this.el.dom.style.top = box.y + "px";
44042             this.updateBody(box.width, box.height);
44043         }else{
44044             this.collapsedEl.dom.style.left = box.x + "px";
44045             this.collapsedEl.dom.style.top = box.y + "px";
44046             this.collapsedEl.setSize(box.width, box.height);
44047         }
44048         if(this.tabs){
44049             this.tabs.autoSizeTabs();
44050         }
44051     },
44052
44053     updateBody : function(w, h){
44054         if(w !== null){
44055             this.el.setWidth(w);
44056             w -= this.el.getBorderWidth("rl");
44057             if(this.config.adjustments){
44058                 w += this.config.adjustments[0];
44059             }
44060         }
44061         if(h !== null){
44062             this.el.setHeight(h);
44063             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44064             h -= this.el.getBorderWidth("tb");
44065             if(this.config.adjustments){
44066                 h += this.config.adjustments[1];
44067             }
44068             this.bodyEl.setHeight(h);
44069             if(this.tabs){
44070                 h = this.tabs.syncHeight(h);
44071             }
44072         }
44073         if(this.panelSize){
44074             w = w !== null ? w : this.panelSize.width;
44075             h = h !== null ? h : this.panelSize.height;
44076         }
44077         if(this.activePanel){
44078             var el = this.activePanel.getEl();
44079             w = w !== null ? w : el.getWidth();
44080             h = h !== null ? h : el.getHeight();
44081             this.panelSize = {width: w, height: h};
44082             this.activePanel.setSize(w, h);
44083         }
44084         if(Roo.isIE && this.tabs){
44085             this.tabs.el.repaint();
44086         }
44087     },
44088
44089     /**
44090      * Returns the container element for this region.
44091      * @return {Roo.Element}
44092      */
44093     getEl : function(){
44094         return this.el;
44095     },
44096
44097     /**
44098      * Hides this region.
44099      */
44100     hide : function(){
44101         if(!this.collapsed){
44102             this.el.dom.style.left = "-2000px";
44103             this.el.hide();
44104         }else{
44105             this.collapsedEl.dom.style.left = "-2000px";
44106             this.collapsedEl.hide();
44107         }
44108         this.visible = false;
44109         this.fireEvent("visibilitychange", this, false);
44110     },
44111
44112     /**
44113      * Shows this region if it was previously hidden.
44114      */
44115     show : function(){
44116         if(!this.collapsed){
44117             this.el.show();
44118         }else{
44119             this.collapsedEl.show();
44120         }
44121         this.visible = true;
44122         this.fireEvent("visibilitychange", this, true);
44123     },
44124
44125     closeClicked : function(){
44126         if(this.activePanel){
44127             this.remove(this.activePanel);
44128         }
44129     },
44130
44131     collapseClick : function(e){
44132         if(this.isSlid){
44133            e.stopPropagation();
44134            this.slideIn();
44135         }else{
44136            e.stopPropagation();
44137            this.slideOut();
44138         }
44139     },
44140
44141     /**
44142      * Collapses this region.
44143      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44144      */
44145     collapse : function(skipAnim){
44146         if(this.collapsed) return;
44147         this.collapsed = true;
44148         if(this.split){
44149             this.split.el.hide();
44150         }
44151         if(this.config.animate && skipAnim !== true){
44152             this.fireEvent("invalidated", this);
44153             this.animateCollapse();
44154         }else{
44155             this.el.setLocation(-20000,-20000);
44156             this.el.hide();
44157             this.collapsedEl.show();
44158             this.fireEvent("collapsed", this);
44159             this.fireEvent("invalidated", this);
44160         }
44161     },
44162
44163     animateCollapse : function(){
44164         // overridden
44165     },
44166
44167     /**
44168      * Expands this region if it was previously collapsed.
44169      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44170      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44171      */
44172     expand : function(e, skipAnim){
44173         if(e) e.stopPropagation();
44174         if(!this.collapsed || this.el.hasActiveFx()) return;
44175         if(this.isSlid){
44176             this.afterSlideIn();
44177             skipAnim = true;
44178         }
44179         this.collapsed = false;
44180         if(this.config.animate && skipAnim !== true){
44181             this.animateExpand();
44182         }else{
44183             this.el.show();
44184             if(this.split){
44185                 this.split.el.show();
44186             }
44187             this.collapsedEl.setLocation(-2000,-2000);
44188             this.collapsedEl.hide();
44189             this.fireEvent("invalidated", this);
44190             this.fireEvent("expanded", this);
44191         }
44192     },
44193
44194     animateExpand : function(){
44195         // overridden
44196     },
44197
44198     initTabs : function(){
44199         this.bodyEl.setStyle("overflow", "hidden");
44200         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44201             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44202             disableTooltips: this.config.disableTabTips
44203         });
44204         if(this.config.hideTabs){
44205             ts.stripWrap.setDisplayed(false);
44206         }
44207         this.tabs = ts;
44208         ts.resizeTabs = this.config.resizeTabs === true;
44209         ts.minTabWidth = this.config.minTabWidth || 40;
44210         ts.maxTabWidth = this.config.maxTabWidth || 250;
44211         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44212         ts.monitorResize = false;
44213         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44214         ts.bodyEl.addClass('x-layout-tabs-body');
44215         this.panels.each(this.initPanelAsTab, this);
44216     },
44217
44218     initPanelAsTab : function(panel){
44219         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44220                     this.config.closeOnTab && panel.isClosable());
44221         if(panel.tabTip !== undefined){
44222             ti.setTooltip(panel.tabTip);
44223         }
44224         ti.on("activate", function(){
44225               this.setActivePanel(panel);
44226         }, this);
44227         if(this.config.closeOnTab){
44228             ti.on("beforeclose", function(t, e){
44229                 e.cancel = true;
44230                 this.remove(panel);
44231             }, this);
44232         }
44233         return ti;
44234     },
44235
44236     updatePanelTitle : function(panel, title){
44237         if(this.activePanel == panel){
44238             this.updateTitle(title);
44239         }
44240         if(this.tabs){
44241             var ti = this.tabs.getTab(panel.getEl().id);
44242             ti.setText(title);
44243             if(panel.tabTip !== undefined){
44244                 ti.setTooltip(panel.tabTip);
44245             }
44246         }
44247     },
44248
44249     updateTitle : function(title){
44250         if(this.titleTextEl && !this.config.title){
44251             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44252         }
44253     },
44254
44255     setActivePanel : function(panel){
44256         panel = this.getPanel(panel);
44257         if(this.activePanel && this.activePanel != panel){
44258             this.activePanel.setActiveState(false);
44259         }
44260         this.activePanel = panel;
44261         panel.setActiveState(true);
44262         if(this.panelSize){
44263             panel.setSize(this.panelSize.width, this.panelSize.height);
44264         }
44265         if(this.closeBtn){
44266             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44267         }
44268         this.updateTitle(panel.getTitle());
44269         if(this.tabs){
44270             this.fireEvent("invalidated", this);
44271         }
44272         this.fireEvent("panelactivated", this, panel);
44273     },
44274
44275     /**
44276      * Shows the specified panel.
44277      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44278      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44279      */
44280     showPanel : function(panel){
44281         if(panel = this.getPanel(panel)){
44282             if(this.tabs){
44283                 var tab = this.tabs.getTab(panel.getEl().id);
44284                 if(tab.isHidden()){
44285                     this.tabs.unhideTab(tab.id);
44286                 }
44287                 tab.activate();
44288             }else{
44289                 this.setActivePanel(panel);
44290             }
44291         }
44292         return panel;
44293     },
44294
44295     /**
44296      * Get the active panel for this region.
44297      * @return {Roo.ContentPanel} The active panel or null
44298      */
44299     getActivePanel : function(){
44300         return this.activePanel;
44301     },
44302
44303     validateVisibility : function(){
44304         if(this.panels.getCount() < 1){
44305             this.updateTitle("&#160;");
44306             this.closeBtn.hide();
44307             this.hide();
44308         }else{
44309             if(!this.isVisible()){
44310                 this.show();
44311             }
44312         }
44313     },
44314
44315     /**
44316      * Adds the passed ContentPanel(s) to this region.
44317      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44318      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44319      */
44320     add : function(panel){
44321         if(arguments.length > 1){
44322             for(var i = 0, len = arguments.length; i < len; i++) {
44323                 this.add(arguments[i]);
44324             }
44325             return null;
44326         }
44327         if(this.hasPanel(panel)){
44328             this.showPanel(panel);
44329             return panel;
44330         }
44331         panel.setRegion(this);
44332         this.panels.add(panel);
44333         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44334             this.bodyEl.dom.appendChild(panel.getEl().dom);
44335             if(panel.background !== true){
44336                 this.setActivePanel(panel);
44337             }
44338             this.fireEvent("paneladded", this, panel);
44339             return panel;
44340         }
44341         if(!this.tabs){
44342             this.initTabs();
44343         }else{
44344             this.initPanelAsTab(panel);
44345         }
44346         if(panel.background !== true){
44347             this.tabs.activate(panel.getEl().id);
44348         }
44349         this.fireEvent("paneladded", this, panel);
44350         return panel;
44351     },
44352
44353     /**
44354      * Hides the tab for the specified panel.
44355      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44356      */
44357     hidePanel : function(panel){
44358         if(this.tabs && (panel = this.getPanel(panel))){
44359             this.tabs.hideTab(panel.getEl().id);
44360         }
44361     },
44362
44363     /**
44364      * Unhides the tab for a previously hidden panel.
44365      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44366      */
44367     unhidePanel : function(panel){
44368         if(this.tabs && (panel = this.getPanel(panel))){
44369             this.tabs.unhideTab(panel.getEl().id);
44370         }
44371     },
44372
44373     clearPanels : function(){
44374         while(this.panels.getCount() > 0){
44375              this.remove(this.panels.first());
44376         }
44377     },
44378
44379     /**
44380      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44381      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44382      * @param {Boolean} preservePanel Overrides the config preservePanel option
44383      * @return {Roo.ContentPanel} The panel that was removed
44384      */
44385     remove : function(panel, preservePanel){
44386         panel = this.getPanel(panel);
44387         if(!panel){
44388             return null;
44389         }
44390         var e = {};
44391         this.fireEvent("beforeremove", this, panel, e);
44392         if(e.cancel === true){
44393             return null;
44394         }
44395         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44396         var panelId = panel.getId();
44397         this.panels.removeKey(panelId);
44398         if(preservePanel){
44399             document.body.appendChild(panel.getEl().dom);
44400         }
44401         if(this.tabs){
44402             this.tabs.removeTab(panel.getEl().id);
44403         }else if (!preservePanel){
44404             this.bodyEl.dom.removeChild(panel.getEl().dom);
44405         }
44406         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44407             var p = this.panels.first();
44408             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44409             tempEl.appendChild(p.getEl().dom);
44410             this.bodyEl.update("");
44411             this.bodyEl.dom.appendChild(p.getEl().dom);
44412             tempEl = null;
44413             this.updateTitle(p.getTitle());
44414             this.tabs = null;
44415             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44416             this.setActivePanel(p);
44417         }
44418         panel.setRegion(null);
44419         if(this.activePanel == panel){
44420             this.activePanel = null;
44421         }
44422         if(this.config.autoDestroy !== false && preservePanel !== true){
44423             try{panel.destroy();}catch(e){}
44424         }
44425         this.fireEvent("panelremoved", this, panel);
44426         return panel;
44427     },
44428
44429     /**
44430      * Returns the TabPanel component used by this region
44431      * @return {Roo.TabPanel}
44432      */
44433     getTabs : function(){
44434         return this.tabs;
44435     },
44436
44437     createTool : function(parentEl, className){
44438         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44439             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44440         btn.addClassOnOver("x-layout-tools-button-over");
44441         return btn;
44442     }
44443 });/*
44444  * Based on:
44445  * Ext JS Library 1.1.1
44446  * Copyright(c) 2006-2007, Ext JS, LLC.
44447  *
44448  * Originally Released Under LGPL - original licence link has changed is not relivant.
44449  *
44450  * Fork - LGPL
44451  * <script type="text/javascript">
44452  */
44453  
44454
44455
44456 /**
44457  * @class Roo.SplitLayoutRegion
44458  * @extends Roo.LayoutRegion
44459  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44460  */
44461 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44462     this.cursor = cursor;
44463     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44464 };
44465
44466 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44467     splitTip : "Drag to resize.",
44468     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44469     useSplitTips : false,
44470
44471     applyConfig : function(config){
44472         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44473         if(config.split){
44474             if(!this.split){
44475                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44476                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44477                 /** The SplitBar for this region 
44478                 * @type Roo.SplitBar */
44479                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44480                 this.split.on("moved", this.onSplitMove, this);
44481                 this.split.useShim = config.useShim === true;
44482                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44483                 if(this.useSplitTips){
44484                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44485                 }
44486                 if(config.collapsible){
44487                     this.split.el.on("dblclick", this.collapse,  this);
44488                 }
44489             }
44490             if(typeof config.minSize != "undefined"){
44491                 this.split.minSize = config.minSize;
44492             }
44493             if(typeof config.maxSize != "undefined"){
44494                 this.split.maxSize = config.maxSize;
44495             }
44496             if(config.hideWhenEmpty || config.hidden || config.collapsed){
44497                 this.hideSplitter();
44498             }
44499         }
44500     },
44501
44502     getHMaxSize : function(){
44503          var cmax = this.config.maxSize || 10000;
44504          var center = this.mgr.getRegion("center");
44505          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44506     },
44507
44508     getVMaxSize : function(){
44509          var cmax = this.config.maxSize || 10000;
44510          var center = this.mgr.getRegion("center");
44511          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44512     },
44513
44514     onSplitMove : function(split, newSize){
44515         this.fireEvent("resized", this, newSize);
44516     },
44517     
44518     /** 
44519      * Returns the {@link Roo.SplitBar} for this region.
44520      * @return {Roo.SplitBar}
44521      */
44522     getSplitBar : function(){
44523         return this.split;
44524     },
44525     
44526     hide : function(){
44527         this.hideSplitter();
44528         Roo.SplitLayoutRegion.superclass.hide.call(this);
44529     },
44530
44531     hideSplitter : function(){
44532         if(this.split){
44533             this.split.el.setLocation(-2000,-2000);
44534             this.split.el.hide();
44535         }
44536     },
44537
44538     show : function(){
44539         if(this.split){
44540             this.split.el.show();
44541         }
44542         Roo.SplitLayoutRegion.superclass.show.call(this);
44543     },
44544     
44545     beforeSlide: function(){
44546         if(Roo.isGecko){// firefox overflow auto bug workaround
44547             this.bodyEl.clip();
44548             if(this.tabs) this.tabs.bodyEl.clip();
44549             if(this.activePanel){
44550                 this.activePanel.getEl().clip();
44551                 
44552                 if(this.activePanel.beforeSlide){
44553                     this.activePanel.beforeSlide();
44554                 }
44555             }
44556         }
44557     },
44558     
44559     afterSlide : function(){
44560         if(Roo.isGecko){// firefox overflow auto bug workaround
44561             this.bodyEl.unclip();
44562             if(this.tabs) this.tabs.bodyEl.unclip();
44563             if(this.activePanel){
44564                 this.activePanel.getEl().unclip();
44565                 if(this.activePanel.afterSlide){
44566                     this.activePanel.afterSlide();
44567                 }
44568             }
44569         }
44570     },
44571
44572     initAutoHide : function(){
44573         if(this.autoHide !== false){
44574             if(!this.autoHideHd){
44575                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44576                 this.autoHideHd = {
44577                     "mouseout": function(e){
44578                         if(!e.within(this.el, true)){
44579                             st.delay(500);
44580                         }
44581                     },
44582                     "mouseover" : function(e){
44583                         st.cancel();
44584                     },
44585                     scope : this
44586                 };
44587             }
44588             this.el.on(this.autoHideHd);
44589         }
44590     },
44591
44592     clearAutoHide : function(){
44593         if(this.autoHide !== false){
44594             this.el.un("mouseout", this.autoHideHd.mouseout);
44595             this.el.un("mouseover", this.autoHideHd.mouseover);
44596         }
44597     },
44598
44599     clearMonitor : function(){
44600         Roo.get(document).un("click", this.slideInIf, this);
44601     },
44602
44603     // these names are backwards but not changed for compat
44604     slideOut : function(){
44605         if(this.isSlid || this.el.hasActiveFx()){
44606             return;
44607         }
44608         this.isSlid = true;
44609         if(this.collapseBtn){
44610             this.collapseBtn.hide();
44611         }
44612         this.closeBtnState = this.closeBtn.getStyle('display');
44613         this.closeBtn.hide();
44614         if(this.stickBtn){
44615             this.stickBtn.show();
44616         }
44617         this.el.show();
44618         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44619         this.beforeSlide();
44620         this.el.setStyle("z-index", 10001);
44621         this.el.slideIn(this.getSlideAnchor(), {
44622             callback: function(){
44623                 this.afterSlide();
44624                 this.initAutoHide();
44625                 Roo.get(document).on("click", this.slideInIf, this);
44626                 this.fireEvent("slideshow", this);
44627             },
44628             scope: this,
44629             block: true
44630         });
44631     },
44632
44633     afterSlideIn : function(){
44634         this.clearAutoHide();
44635         this.isSlid = false;
44636         this.clearMonitor();
44637         this.el.setStyle("z-index", "");
44638         if(this.collapseBtn){
44639             this.collapseBtn.show();
44640         }
44641         this.closeBtn.setStyle('display', this.closeBtnState);
44642         if(this.stickBtn){
44643             this.stickBtn.hide();
44644         }
44645         this.fireEvent("slidehide", this);
44646     },
44647
44648     slideIn : function(cb){
44649         if(!this.isSlid || this.el.hasActiveFx()){
44650             Roo.callback(cb);
44651             return;
44652         }
44653         this.isSlid = false;
44654         this.beforeSlide();
44655         this.el.slideOut(this.getSlideAnchor(), {
44656             callback: function(){
44657                 this.el.setLeftTop(-10000, -10000);
44658                 this.afterSlide();
44659                 this.afterSlideIn();
44660                 Roo.callback(cb);
44661             },
44662             scope: this,
44663             block: true
44664         });
44665     },
44666     
44667     slideInIf : function(e){
44668         if(!e.within(this.el)){
44669             this.slideIn();
44670         }
44671     },
44672
44673     animateCollapse : function(){
44674         this.beforeSlide();
44675         this.el.setStyle("z-index", 20000);
44676         var anchor = this.getSlideAnchor();
44677         this.el.slideOut(anchor, {
44678             callback : function(){
44679                 this.el.setStyle("z-index", "");
44680                 this.collapsedEl.slideIn(anchor, {duration:.3});
44681                 this.afterSlide();
44682                 this.el.setLocation(-10000,-10000);
44683                 this.el.hide();
44684                 this.fireEvent("collapsed", this);
44685             },
44686             scope: this,
44687             block: true
44688         });
44689     },
44690
44691     animateExpand : function(){
44692         this.beforeSlide();
44693         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44694         this.el.setStyle("z-index", 20000);
44695         this.collapsedEl.hide({
44696             duration:.1
44697         });
44698         this.el.slideIn(this.getSlideAnchor(), {
44699             callback : function(){
44700                 this.el.setStyle("z-index", "");
44701                 this.afterSlide();
44702                 if(this.split){
44703                     this.split.el.show();
44704                 }
44705                 this.fireEvent("invalidated", this);
44706                 this.fireEvent("expanded", this);
44707             },
44708             scope: this,
44709             block: true
44710         });
44711     },
44712
44713     anchors : {
44714         "west" : "left",
44715         "east" : "right",
44716         "north" : "top",
44717         "south" : "bottom"
44718     },
44719
44720     sanchors : {
44721         "west" : "l",
44722         "east" : "r",
44723         "north" : "t",
44724         "south" : "b"
44725     },
44726
44727     canchors : {
44728         "west" : "tl-tr",
44729         "east" : "tr-tl",
44730         "north" : "tl-bl",
44731         "south" : "bl-tl"
44732     },
44733
44734     getAnchor : function(){
44735         return this.anchors[this.position];
44736     },
44737
44738     getCollapseAnchor : function(){
44739         return this.canchors[this.position];
44740     },
44741
44742     getSlideAnchor : function(){
44743         return this.sanchors[this.position];
44744     },
44745
44746     getAlignAdj : function(){
44747         var cm = this.cmargins;
44748         switch(this.position){
44749             case "west":
44750                 return [0, 0];
44751             break;
44752             case "east":
44753                 return [0, 0];
44754             break;
44755             case "north":
44756                 return [0, 0];
44757             break;
44758             case "south":
44759                 return [0, 0];
44760             break;
44761         }
44762     },
44763
44764     getExpandAdj : function(){
44765         var c = this.collapsedEl, cm = this.cmargins;
44766         switch(this.position){
44767             case "west":
44768                 return [-(cm.right+c.getWidth()+cm.left), 0];
44769             break;
44770             case "east":
44771                 return [cm.right+c.getWidth()+cm.left, 0];
44772             break;
44773             case "north":
44774                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44775             break;
44776             case "south":
44777                 return [0, cm.top+cm.bottom+c.getHeight()];
44778             break;
44779         }
44780     }
44781 });/*
44782  * Based on:
44783  * Ext JS Library 1.1.1
44784  * Copyright(c) 2006-2007, Ext JS, LLC.
44785  *
44786  * Originally Released Under LGPL - original licence link has changed is not relivant.
44787  *
44788  * Fork - LGPL
44789  * <script type="text/javascript">
44790  */
44791 /*
44792  * These classes are private internal classes
44793  */
44794 Roo.CenterLayoutRegion = function(mgr, config){
44795     Roo.LayoutRegion.call(this, mgr, config, "center");
44796     this.visible = true;
44797     this.minWidth = config.minWidth || 20;
44798     this.minHeight = config.minHeight || 20;
44799 };
44800
44801 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
44802     hide : function(){
44803         // center panel can't be hidden
44804     },
44805     
44806     show : function(){
44807         // center panel can't be hidden
44808     },
44809     
44810     getMinWidth: function(){
44811         return this.minWidth;
44812     },
44813     
44814     getMinHeight: function(){
44815         return this.minHeight;
44816     }
44817 });
44818
44819
44820 Roo.NorthLayoutRegion = function(mgr, config){
44821     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
44822     if(this.split){
44823         this.split.placement = Roo.SplitBar.TOP;
44824         this.split.orientation = Roo.SplitBar.VERTICAL;
44825         this.split.el.addClass("x-layout-split-v");
44826     }
44827     var size = config.initialSize || config.height;
44828     if(typeof size != "undefined"){
44829         this.el.setHeight(size);
44830     }
44831 };
44832 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
44833     orientation: Roo.SplitBar.VERTICAL,
44834     getBox : function(){
44835         if(this.collapsed){
44836             return this.collapsedEl.getBox();
44837         }
44838         var box = this.el.getBox();
44839         if(this.split){
44840             box.height += this.split.el.getHeight();
44841         }
44842         return box;
44843     },
44844     
44845     updateBox : function(box){
44846         if(this.split && !this.collapsed){
44847             box.height -= this.split.el.getHeight();
44848             this.split.el.setLeft(box.x);
44849             this.split.el.setTop(box.y+box.height);
44850             this.split.el.setWidth(box.width);
44851         }
44852         if(this.collapsed){
44853             this.updateBody(box.width, null);
44854         }
44855         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44856     }
44857 });
44858
44859 Roo.SouthLayoutRegion = function(mgr, config){
44860     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
44861     if(this.split){
44862         this.split.placement = Roo.SplitBar.BOTTOM;
44863         this.split.orientation = Roo.SplitBar.VERTICAL;
44864         this.split.el.addClass("x-layout-split-v");
44865     }
44866     var size = config.initialSize || config.height;
44867     if(typeof size != "undefined"){
44868         this.el.setHeight(size);
44869     }
44870 };
44871 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
44872     orientation: Roo.SplitBar.VERTICAL,
44873     getBox : function(){
44874         if(this.collapsed){
44875             return this.collapsedEl.getBox();
44876         }
44877         var box = this.el.getBox();
44878         if(this.split){
44879             var sh = this.split.el.getHeight();
44880             box.height += sh;
44881             box.y -= sh;
44882         }
44883         return box;
44884     },
44885     
44886     updateBox : function(box){
44887         if(this.split && !this.collapsed){
44888             var sh = this.split.el.getHeight();
44889             box.height -= sh;
44890             box.y += sh;
44891             this.split.el.setLeft(box.x);
44892             this.split.el.setTop(box.y-sh);
44893             this.split.el.setWidth(box.width);
44894         }
44895         if(this.collapsed){
44896             this.updateBody(box.width, null);
44897         }
44898         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44899     }
44900 });
44901
44902 Roo.EastLayoutRegion = function(mgr, config){
44903     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44904     if(this.split){
44905         this.split.placement = Roo.SplitBar.RIGHT;
44906         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44907         this.split.el.addClass("x-layout-split-h");
44908     }
44909     var size = config.initialSize || config.width;
44910     if(typeof size != "undefined"){
44911         this.el.setWidth(size);
44912     }
44913 };
44914 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44915     orientation: Roo.SplitBar.HORIZONTAL,
44916     getBox : function(){
44917         if(this.collapsed){
44918             return this.collapsedEl.getBox();
44919         }
44920         var box = this.el.getBox();
44921         if(this.split){
44922             var sw = this.split.el.getWidth();
44923             box.width += sw;
44924             box.x -= sw;
44925         }
44926         return box;
44927     },
44928
44929     updateBox : function(box){
44930         if(this.split && !this.collapsed){
44931             var sw = this.split.el.getWidth();
44932             box.width -= sw;
44933             this.split.el.setLeft(box.x);
44934             this.split.el.setTop(box.y);
44935             this.split.el.setHeight(box.height);
44936             box.x += sw;
44937         }
44938         if(this.collapsed){
44939             this.updateBody(null, box.height);
44940         }
44941         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44942     }
44943 });
44944
44945 Roo.WestLayoutRegion = function(mgr, config){
44946     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44947     if(this.split){
44948         this.split.placement = Roo.SplitBar.LEFT;
44949         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44950         this.split.el.addClass("x-layout-split-h");
44951     }
44952     var size = config.initialSize || config.width;
44953     if(typeof size != "undefined"){
44954         this.el.setWidth(size);
44955     }
44956 };
44957 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44958     orientation: Roo.SplitBar.HORIZONTAL,
44959     getBox : function(){
44960         if(this.collapsed){
44961             return this.collapsedEl.getBox();
44962         }
44963         var box = this.el.getBox();
44964         if(this.split){
44965             box.width += this.split.el.getWidth();
44966         }
44967         return box;
44968     },
44969     
44970     updateBox : function(box){
44971         if(this.split && !this.collapsed){
44972             var sw = this.split.el.getWidth();
44973             box.width -= sw;
44974             this.split.el.setLeft(box.x+box.width);
44975             this.split.el.setTop(box.y);
44976             this.split.el.setHeight(box.height);
44977         }
44978         if(this.collapsed){
44979             this.updateBody(null, box.height);
44980         }
44981         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44982     }
44983 });
44984 /*
44985  * Based on:
44986  * Ext JS Library 1.1.1
44987  * Copyright(c) 2006-2007, Ext JS, LLC.
44988  *
44989  * Originally Released Under LGPL - original licence link has changed is not relivant.
44990  *
44991  * Fork - LGPL
44992  * <script type="text/javascript">
44993  */
44994  
44995  
44996 /*
44997  * Private internal class for reading and applying state
44998  */
44999 Roo.LayoutStateManager = function(layout){
45000      // default empty state
45001      this.state = {
45002         north: {},
45003         south: {},
45004         east: {},
45005         west: {}       
45006     };
45007 };
45008
45009 Roo.LayoutStateManager.prototype = {
45010     init : function(layout, provider){
45011         this.provider = provider;
45012         var state = provider.get(layout.id+"-layout-state");
45013         if(state){
45014             var wasUpdating = layout.isUpdating();
45015             if(!wasUpdating){
45016                 layout.beginUpdate();
45017             }
45018             for(var key in state){
45019                 if(typeof state[key] != "function"){
45020                     var rstate = state[key];
45021                     var r = layout.getRegion(key);
45022                     if(r && rstate){
45023                         if(rstate.size){
45024                             r.resizeTo(rstate.size);
45025                         }
45026                         if(rstate.collapsed == true){
45027                             r.collapse(true);
45028                         }else{
45029                             r.expand(null, true);
45030                         }
45031                     }
45032                 }
45033             }
45034             if(!wasUpdating){
45035                 layout.endUpdate();
45036             }
45037             this.state = state; 
45038         }
45039         this.layout = layout;
45040         layout.on("regionresized", this.onRegionResized, this);
45041         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45042         layout.on("regionexpanded", this.onRegionExpanded, this);
45043     },
45044     
45045     storeState : function(){
45046         this.provider.set(this.layout.id+"-layout-state", this.state);
45047     },
45048     
45049     onRegionResized : function(region, newSize){
45050         this.state[region.getPosition()].size = newSize;
45051         this.storeState();
45052     },
45053     
45054     onRegionCollapsed : function(region){
45055         this.state[region.getPosition()].collapsed = true;
45056         this.storeState();
45057     },
45058     
45059     onRegionExpanded : function(region){
45060         this.state[region.getPosition()].collapsed = false;
45061         this.storeState();
45062     }
45063 };/*
45064  * Based on:
45065  * Ext JS Library 1.1.1
45066  * Copyright(c) 2006-2007, Ext JS, LLC.
45067  *
45068  * Originally Released Under LGPL - original licence link has changed is not relivant.
45069  *
45070  * Fork - LGPL
45071  * <script type="text/javascript">
45072  */
45073 /**
45074  * @class Roo.ContentPanel
45075  * @extends Roo.util.Observable
45076  * A basic ContentPanel element.
45077  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45078  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45079  * @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
45080  * @cfg {Boolean} closable True if the panel can be closed/removed
45081  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45082  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45083  * @cfg {Toolbar} toolbar A toolbar for this panel
45084  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45085  * @cfg {String} title The title for this panel
45086  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45087  * @cfg {String} url Calls {@link #setUrl} with this value
45088  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45089  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45090  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45091  * @constructor
45092  * Create a new ContentPanel.
45093  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45094  * @param {String/Object} config A string to set only the title or a config object
45095  * @param {String} content (optional) Set the HTML content for this panel
45096  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45097  */
45098 Roo.ContentPanel = function(el, config, content){
45099     
45100      
45101     /*
45102     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45103         config = el;
45104         el = Roo.id();
45105     }
45106     if (config && config.parentLayout) { 
45107         el = config.parentLayout.el.createChild(); 
45108     }
45109     */
45110     if(el.autoCreate){ // xtype is available if this is called from factory
45111         config = el;
45112         el = Roo.id();
45113     }
45114     this.el = Roo.get(el);
45115     if(!this.el && config && config.autoCreate){
45116         if(typeof config.autoCreate == "object"){
45117             if(!config.autoCreate.id){
45118                 config.autoCreate.id = config.id||el;
45119             }
45120             this.el = Roo.DomHelper.append(document.body,
45121                         config.autoCreate, true);
45122         }else{
45123             this.el = Roo.DomHelper.append(document.body,
45124                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45125         }
45126     }
45127     this.closable = false;
45128     this.loaded = false;
45129     this.active = false;
45130     if(typeof config == "string"){
45131         this.title = config;
45132     }else{
45133         Roo.apply(this, config);
45134     }
45135     
45136     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45137         this.wrapEl = this.el.wrap();    
45138         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45139         
45140     }
45141     
45142     
45143     
45144     if(this.resizeEl){
45145         this.resizeEl = Roo.get(this.resizeEl, true);
45146     }else{
45147         this.resizeEl = this.el;
45148     }
45149     this.addEvents({
45150         /**
45151          * @event activate
45152          * Fires when this panel is activated. 
45153          * @param {Roo.ContentPanel} this
45154          */
45155         "activate" : true,
45156         /**
45157          * @event deactivate
45158          * Fires when this panel is activated. 
45159          * @param {Roo.ContentPanel} this
45160          */
45161         "deactivate" : true,
45162
45163         /**
45164          * @event resize
45165          * Fires when this panel is resized if fitToFrame is true.
45166          * @param {Roo.ContentPanel} this
45167          * @param {Number} width The width after any component adjustments
45168          * @param {Number} height The height after any component adjustments
45169          */
45170         "resize" : true
45171     });
45172     if(this.autoScroll){
45173         this.resizeEl.setStyle("overflow", "auto");
45174     }
45175     content = content || this.content;
45176     if(content){
45177         this.setContent(content);
45178     }
45179     if(config && config.url){
45180         this.setUrl(this.url, this.params, this.loadOnce);
45181     }
45182     
45183     
45184     
45185     Roo.ContentPanel.superclass.constructor.call(this);
45186 };
45187
45188 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45189     tabTip:'',
45190     setRegion : function(region){
45191         this.region = region;
45192         if(region){
45193            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45194         }else{
45195            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45196         } 
45197     },
45198     
45199     /**
45200      * Returns the toolbar for this Panel if one was configured. 
45201      * @return {Roo.Toolbar} 
45202      */
45203     getToolbar : function(){
45204         return this.toolbar;
45205     },
45206     
45207     setActiveState : function(active){
45208         this.active = active;
45209         if(!active){
45210             this.fireEvent("deactivate", this);
45211         }else{
45212             this.fireEvent("activate", this);
45213         }
45214     },
45215     /**
45216      * Updates this panel's element
45217      * @param {String} content The new content
45218      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45219     */
45220     setContent : function(content, loadScripts){
45221         this.el.update(content, loadScripts);
45222     },
45223
45224     ignoreResize : function(w, h){
45225         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45226             return true;
45227         }else{
45228             this.lastSize = {width: w, height: h};
45229             return false;
45230         }
45231     },
45232     /**
45233      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45234      * @return {Roo.UpdateManager} The UpdateManager
45235      */
45236     getUpdateManager : function(){
45237         return this.el.getUpdateManager();
45238     },
45239      /**
45240      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45241      * @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:
45242 <pre><code>
45243 panel.load({
45244     url: "your-url.php",
45245     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45246     callback: yourFunction,
45247     scope: yourObject, //(optional scope)
45248     discardUrl: false,
45249     nocache: false,
45250     text: "Loading...",
45251     timeout: 30,
45252     scripts: false
45253 });
45254 </code></pre>
45255      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45256      * 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.
45257      * @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}
45258      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45259      * @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.
45260      * @return {Roo.ContentPanel} this
45261      */
45262     load : function(){
45263         var um = this.el.getUpdateManager();
45264         um.update.apply(um, arguments);
45265         return this;
45266     },
45267
45268
45269     /**
45270      * 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.
45271      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45272      * @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)
45273      * @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)
45274      * @return {Roo.UpdateManager} The UpdateManager
45275      */
45276     setUrl : function(url, params, loadOnce){
45277         if(this.refreshDelegate){
45278             this.removeListener("activate", this.refreshDelegate);
45279         }
45280         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45281         this.on("activate", this.refreshDelegate);
45282         return this.el.getUpdateManager();
45283     },
45284     
45285     _handleRefresh : function(url, params, loadOnce){
45286         if(!loadOnce || !this.loaded){
45287             var updater = this.el.getUpdateManager();
45288             updater.update(url, params, this._setLoaded.createDelegate(this));
45289         }
45290     },
45291     
45292     _setLoaded : function(){
45293         this.loaded = true;
45294     }, 
45295     
45296     /**
45297      * Returns this panel's id
45298      * @return {String} 
45299      */
45300     getId : function(){
45301         return this.el.id;
45302     },
45303     
45304     /** 
45305      * Returns this panel's element - used by regiosn to add.
45306      * @return {Roo.Element} 
45307      */
45308     getEl : function(){
45309         return this.wrapEl || this.el;
45310     },
45311     
45312     adjustForComponents : function(width, height){
45313         if(this.resizeEl != this.el){
45314             width -= this.el.getFrameWidth('lr');
45315             height -= this.el.getFrameWidth('tb');
45316         }
45317         if(this.toolbar){
45318             var te = this.toolbar.getEl();
45319             height -= te.getHeight();
45320             te.setWidth(width);
45321         }
45322         if(this.adjustments){
45323             width += this.adjustments[0];
45324             height += this.adjustments[1];
45325         }
45326         return {"width": width, "height": height};
45327     },
45328     
45329     setSize : function(width, height){
45330         if(this.fitToFrame && !this.ignoreResize(width, height)){
45331             if(this.fitContainer && this.resizeEl != this.el){
45332                 this.el.setSize(width, height);
45333             }
45334             var size = this.adjustForComponents(width, height);
45335             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45336             this.fireEvent('resize', this, size.width, size.height);
45337         }
45338     },
45339     
45340     /**
45341      * Returns this panel's title
45342      * @return {String} 
45343      */
45344     getTitle : function(){
45345         return this.title;
45346     },
45347     
45348     /**
45349      * Set this panel's title
45350      * @param {String} title
45351      */
45352     setTitle : function(title){
45353         this.title = title;
45354         if(this.region){
45355             this.region.updatePanelTitle(this, title);
45356         }
45357     },
45358     
45359     /**
45360      * Returns true is this panel was configured to be closable
45361      * @return {Boolean} 
45362      */
45363     isClosable : function(){
45364         return this.closable;
45365     },
45366     
45367     beforeSlide : function(){
45368         this.el.clip();
45369         this.resizeEl.clip();
45370     },
45371     
45372     afterSlide : function(){
45373         this.el.unclip();
45374         this.resizeEl.unclip();
45375     },
45376     
45377     /**
45378      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45379      *   Will fail silently if the {@link #setUrl} method has not been called.
45380      *   This does not activate the panel, just updates its content.
45381      */
45382     refresh : function(){
45383         if(this.refreshDelegate){
45384            this.loaded = false;
45385            this.refreshDelegate();
45386         }
45387     },
45388     
45389     /**
45390      * Destroys this panel
45391      */
45392     destroy : function(){
45393         this.el.removeAllListeners();
45394         var tempEl = document.createElement("span");
45395         tempEl.appendChild(this.el.dom);
45396         tempEl.innerHTML = "";
45397         this.el.remove();
45398         this.el = null;
45399     },
45400     
45401       /**
45402      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45403      * <pre><code>
45404
45405 layout.addxtype({
45406        xtype : 'Form',
45407        items: [ .... ]
45408    }
45409 );
45410
45411 </code></pre>
45412      * @param {Object} cfg Xtype definition of item to add.
45413      */
45414     
45415     addxtype : function(cfg) {
45416         // add form..
45417         if (cfg.xtype.match(/^Form$/)) {
45418             var el = this.el.createChild();
45419
45420             this.form = new  Roo.form.Form(cfg);
45421             
45422             
45423             if ( this.form.allItems.length) this.form.render(el.dom);
45424             return this.form;
45425         }
45426         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45427             // views..
45428             cfg.el = this.el.appendChild(document.createElement("div"));
45429             // factory?
45430             var ret = new Roo[cfg.xtype](cfg);
45431             ret.render(false, ''); // render blank..
45432             return ret;
45433             
45434         }
45435         return false;
45436         
45437     }
45438 });
45439
45440 /**
45441  * @class Roo.GridPanel
45442  * @extends Roo.ContentPanel
45443  * @constructor
45444  * Create a new GridPanel.
45445  * @param {Roo.grid.Grid} grid The grid for this panel
45446  * @param {String/Object} config A string to set only the panel's title, or a config object
45447  */
45448 Roo.GridPanel = function(grid, config){
45449     
45450   
45451     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45452         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45453         
45454     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45455     
45456     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45457     
45458     if(this.toolbar){
45459         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45460     }
45461     // xtype created footer. - not sure if will work as we normally have to render first..
45462     if (this.footer && !this.footer.el && this.footer.xtype) {
45463         
45464         this.footer.container = this.grid.getView().getFooterPanel(true);
45465         this.footer.dataSource = this.grid.dataSource;
45466         this.footer = Roo.factory(this.footer, Roo);
45467         
45468     }
45469     
45470     grid.monitorWindowResize = false; // turn off autosizing
45471     grid.autoHeight = false;
45472     grid.autoWidth = false;
45473     this.grid = grid;
45474     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45475 };
45476
45477 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45478     getId : function(){
45479         return this.grid.id;
45480     },
45481     
45482     /**
45483      * Returns the grid for this panel
45484      * @return {Roo.grid.Grid} 
45485      */
45486     getGrid : function(){
45487         return this.grid;    
45488     },
45489     
45490     setSize : function(width, height){
45491         if(!this.ignoreResize(width, height)){
45492             var grid = this.grid;
45493             var size = this.adjustForComponents(width, height);
45494             grid.getGridEl().setSize(size.width, size.height);
45495             grid.autoSize();
45496         }
45497     },
45498     
45499     beforeSlide : function(){
45500         this.grid.getView().scroller.clip();
45501     },
45502     
45503     afterSlide : function(){
45504         this.grid.getView().scroller.unclip();
45505     },
45506     
45507     destroy : function(){
45508         this.grid.destroy();
45509         delete this.grid;
45510         Roo.GridPanel.superclass.destroy.call(this); 
45511     }
45512 });
45513
45514
45515 /**
45516  * @class Roo.NestedLayoutPanel
45517  * @extends Roo.ContentPanel
45518  * @constructor
45519  * Create a new NestedLayoutPanel.
45520  * 
45521  * 
45522  * @param {Roo.BorderLayout} layout The layout for this panel
45523  * @param {String/Object} config A string to set only the title or a config object
45524  */
45525 Roo.NestedLayoutPanel = function(layout, config)
45526 {
45527     // construct with only one argument..
45528     /* FIXME - implement nicer consturctors
45529     if (layout.layout) {
45530         config = layout;
45531         layout = config.layout;
45532         delete config.layout;
45533     }
45534     if (layout.xtype && !layout.getEl) {
45535         // then layout needs constructing..
45536         layout = Roo.factory(layout, Roo);
45537     }
45538     */
45539     
45540     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
45541     
45542     layout.monitorWindowResize = false; // turn off autosizing
45543     this.layout = layout;
45544     this.layout.getEl().addClass("x-layout-nested-layout");
45545     
45546     
45547     
45548 };
45549
45550 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
45551
45552     setSize : function(width, height){
45553         if(!this.ignoreResize(width, height)){
45554             var size = this.adjustForComponents(width, height);
45555             var el = this.layout.getEl();
45556             el.setSize(size.width, size.height);
45557             var touch = el.dom.offsetWidth;
45558             this.layout.layout();
45559             // ie requires a double layout on the first pass
45560             if(Roo.isIE && !this.initialized){
45561                 this.initialized = true;
45562                 this.layout.layout();
45563             }
45564         }
45565     },
45566     
45567     // activate all subpanels if not currently active..
45568     
45569     setActiveState : function(active){
45570         this.active = active;
45571         if(!active){
45572             this.fireEvent("deactivate", this);
45573             return;
45574         }
45575         
45576         this.fireEvent("activate", this);
45577         // not sure if this should happen before or after..
45578         if (!this.layout) {
45579             return; // should not happen..
45580         }
45581         var reg = false;
45582         for (var r in this.layout.regions) {
45583             reg = this.layout.getRegion(r);
45584             if (reg.getActivePanel()) {
45585                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45586                 reg.setActivePanel(reg.getActivePanel());
45587                 continue;
45588             }
45589             if (!reg.panels.length) {
45590                 continue;
45591             }
45592             reg.showPanel(reg.getPanel(0));
45593         }
45594         
45595         
45596         
45597         
45598     },
45599     
45600     /**
45601      * Returns the nested BorderLayout for this panel
45602      * @return {Roo.BorderLayout} 
45603      */
45604     getLayout : function(){
45605         return this.layout;
45606     },
45607     
45608      /**
45609      * Adds a xtype elements to the layout of the nested panel
45610      * <pre><code>
45611
45612 panel.addxtype({
45613        xtype : 'ContentPanel',
45614        region: 'west',
45615        items: [ .... ]
45616    }
45617 );
45618
45619 panel.addxtype({
45620         xtype : 'NestedLayoutPanel',
45621         region: 'west',
45622         layout: {
45623            center: { },
45624            west: { }   
45625         },
45626         items : [ ... list of content panels or nested layout panels.. ]
45627    }
45628 );
45629 </code></pre>
45630      * @param {Object} cfg Xtype definition of item to add.
45631      */
45632     addxtype : function(cfg) {
45633         return this.layout.addxtype(cfg);
45634     
45635     }
45636 });
45637
45638 Roo.ScrollPanel = function(el, config, content){
45639     config = config || {};
45640     config.fitToFrame = true;
45641     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45642     
45643     this.el.dom.style.overflow = "hidden";
45644     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45645     this.el.removeClass("x-layout-inactive-content");
45646     this.el.on("mousewheel", this.onWheel, this);
45647
45648     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45649     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45650     up.unselectable(); down.unselectable();
45651     up.on("click", this.scrollUp, this);
45652     down.on("click", this.scrollDown, this);
45653     up.addClassOnOver("x-scroller-btn-over");
45654     down.addClassOnOver("x-scroller-btn-over");
45655     up.addClassOnClick("x-scroller-btn-click");
45656     down.addClassOnClick("x-scroller-btn-click");
45657     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45658
45659     this.resizeEl = this.el;
45660     this.el = wrap; this.up = up; this.down = down;
45661 };
45662
45663 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45664     increment : 100,
45665     wheelIncrement : 5,
45666     scrollUp : function(){
45667         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45668     },
45669
45670     scrollDown : function(){
45671         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45672     },
45673
45674     afterScroll : function(){
45675         var el = this.resizeEl;
45676         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45677         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45678         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45679     },
45680
45681     setSize : function(){
45682         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
45683         this.afterScroll();
45684     },
45685
45686     onWheel : function(e){
45687         var d = e.getWheelDelta();
45688         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45689         this.afterScroll();
45690         e.stopEvent();
45691     },
45692
45693     setContent : function(content, loadScripts){
45694         this.resizeEl.update(content, loadScripts);
45695     }
45696
45697 });
45698
45699
45700
45701
45702
45703
45704
45705
45706
45707 /**
45708  * @class Roo.TreePanel
45709  * @extends Roo.ContentPanel
45710  * @constructor
45711  * Create a new TreePanel. - defaults to fit/scoll contents.
45712  * @param {String/Object} config A string to set only the panel's title, or a config object
45713  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45714  */
45715 Roo.TreePanel = function(config){
45716     var el = config.el;
45717     var tree = config.tree;
45718     delete config.tree; 
45719     delete config.el; // hopefull!
45720     
45721     // wrapper for IE7 strict & safari scroll issue
45722     
45723     var treeEl = el.createChild();
45724     config.resizeEl = treeEl;
45725     
45726     
45727     
45728     Roo.TreePanel.superclass.constructor.call(this, el, config);
45729  
45730  
45731     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45732     //console.log(tree);
45733     this.on('activate', function()
45734     {
45735         if (this.tree.rendered) {
45736             return;
45737         }
45738         //console.log('render tree');
45739         this.tree.render();
45740     });
45741     
45742     this.on('resize',  function (cp, w, h) {
45743             this.tree.innerCt.setWidth(w);
45744             this.tree.innerCt.setHeight(h);
45745             this.tree.innerCt.setStyle('overflow-y', 'auto');
45746     });
45747
45748         
45749     
45750 };
45751
45752 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
45753     fitToFrame : true,
45754     autoScroll : true
45755 });
45756
45757
45758
45759
45760
45761
45762
45763
45764
45765
45766
45767 /*
45768  * Based on:
45769  * Ext JS Library 1.1.1
45770  * Copyright(c) 2006-2007, Ext JS, LLC.
45771  *
45772  * Originally Released Under LGPL - original licence link has changed is not relivant.
45773  *
45774  * Fork - LGPL
45775  * <script type="text/javascript">
45776  */
45777  
45778
45779 /**
45780  * @class Roo.ReaderLayout
45781  * @extends Roo.BorderLayout
45782  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
45783  * center region containing two nested regions (a top one for a list view and one for item preview below),
45784  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
45785  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
45786  * expedites the setup of the overall layout and regions for this common application style.
45787  * Example:
45788  <pre><code>
45789 var reader = new Roo.ReaderLayout();
45790 var CP = Roo.ContentPanel;  // shortcut for adding
45791
45792 reader.beginUpdate();
45793 reader.add("north", new CP("north", "North"));
45794 reader.add("west", new CP("west", {title: "West"}));
45795 reader.add("east", new CP("east", {title: "East"}));
45796
45797 reader.regions.listView.add(new CP("listView", "List"));
45798 reader.regions.preview.add(new CP("preview", "Preview"));
45799 reader.endUpdate();
45800 </code></pre>
45801 * @constructor
45802 * Create a new ReaderLayout
45803 * @param {Object} config Configuration options
45804 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
45805 * document.body if omitted)
45806 */
45807 Roo.ReaderLayout = function(config, renderTo){
45808     var c = config || {size:{}};
45809     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
45810         north: c.north !== false ? Roo.apply({
45811             split:false,
45812             initialSize: 32,
45813             titlebar: false
45814         }, c.north) : false,
45815         west: c.west !== false ? Roo.apply({
45816             split:true,
45817             initialSize: 200,
45818             minSize: 175,
45819             maxSize: 400,
45820             titlebar: true,
45821             collapsible: true,
45822             animate: true,
45823             margins:{left:5,right:0,bottom:5,top:5},
45824             cmargins:{left:5,right:5,bottom:5,top:5}
45825         }, c.west) : false,
45826         east: c.east !== false ? Roo.apply({
45827             split:true,
45828             initialSize: 200,
45829             minSize: 175,
45830             maxSize: 400,
45831             titlebar: true,
45832             collapsible: true,
45833             animate: true,
45834             margins:{left:0,right:5,bottom:5,top:5},
45835             cmargins:{left:5,right:5,bottom:5,top:5}
45836         }, c.east) : false,
45837         center: Roo.apply({
45838             tabPosition: 'top',
45839             autoScroll:false,
45840             closeOnTab: true,
45841             titlebar:false,
45842             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
45843         }, c.center)
45844     });
45845
45846     this.el.addClass('x-reader');
45847
45848     this.beginUpdate();
45849
45850     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
45851         south: c.preview !== false ? Roo.apply({
45852             split:true,
45853             initialSize: 200,
45854             minSize: 100,
45855             autoScroll:true,
45856             collapsible:true,
45857             titlebar: true,
45858             cmargins:{top:5,left:0, right:0, bottom:0}
45859         }, c.preview) : false,
45860         center: Roo.apply({
45861             autoScroll:false,
45862             titlebar:false,
45863             minHeight:200
45864         }, c.listView)
45865     });
45866     this.add('center', new Roo.NestedLayoutPanel(inner,
45867             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
45868
45869     this.endUpdate();
45870
45871     this.regions.preview = inner.getRegion('south');
45872     this.regions.listView = inner.getRegion('center');
45873 };
45874
45875 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
45876  * Based on:
45877  * Ext JS Library 1.1.1
45878  * Copyright(c) 2006-2007, Ext JS, LLC.
45879  *
45880  * Originally Released Under LGPL - original licence link has changed is not relivant.
45881  *
45882  * Fork - LGPL
45883  * <script type="text/javascript">
45884  */
45885  
45886 /**
45887  * @class Roo.grid.Grid
45888  * @extends Roo.util.Observable
45889  * This class represents the primary interface of a component based grid control.
45890  * <br><br>Usage:<pre><code>
45891  var grid = new Roo.grid.Grid("my-container-id", {
45892      ds: myDataStore,
45893      cm: myColModel,
45894      selModel: mySelectionModel,
45895      autoSizeColumns: true,
45896      monitorWindowResize: false,
45897      trackMouseOver: true
45898  });
45899  // set any options
45900  grid.render();
45901  * </code></pre>
45902  * <b>Common Problems:</b><br/>
45903  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
45904  * element will correct this<br/>
45905  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
45906  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
45907  * are unpredictable.<br/>
45908  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
45909  * grid to calculate dimensions/offsets.<br/>
45910   * @constructor
45911  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
45912  * The container MUST have some type of size defined for the grid to fill. The container will be
45913  * automatically set to position relative if it isn't already.
45914  * @param {Object} config A config object that sets properties on this grid.
45915  */
45916 Roo.grid.Grid = function(container, config){
45917         // initialize the container
45918         this.container = Roo.get(container);
45919         this.container.update("");
45920         this.container.setStyle("overflow", "hidden");
45921     this.container.addClass('x-grid-container');
45922
45923     this.id = this.container.id;
45924
45925     Roo.apply(this, config);
45926     // check and correct shorthanded configs
45927     if(this.ds){
45928         this.dataSource = this.ds;
45929         delete this.ds;
45930     }
45931     if(this.cm){
45932         this.colModel = this.cm;
45933         delete this.cm;
45934     }
45935     if(this.sm){
45936         this.selModel = this.sm;
45937         delete this.sm;
45938     }
45939
45940     if (this.selModel) {
45941         this.selModel = Roo.factory(this.selModel, Roo.grid);
45942         this.sm = this.selModel;
45943         this.sm.xmodule = this.xmodule || false;
45944     }
45945     if (typeof(this.colModel.config) == 'undefined') {
45946         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45947         this.cm = this.colModel;
45948         this.cm.xmodule = this.xmodule || false;
45949     }
45950     if (this.dataSource) {
45951         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45952         this.ds = this.dataSource;
45953         this.ds.xmodule = this.xmodule || false;
45954         
45955     }
45956     
45957     
45958     
45959     if(this.width){
45960         this.container.setWidth(this.width);
45961     }
45962
45963     if(this.height){
45964         this.container.setHeight(this.height);
45965     }
45966     /** @private */
45967         this.addEvents({
45968             // raw events
45969             /**
45970              * @event click
45971              * The raw click event for the entire grid.
45972              * @param {Roo.EventObject} e
45973              */
45974             "click" : true,
45975             /**
45976              * @event dblclick
45977              * The raw dblclick event for the entire grid.
45978              * @param {Roo.EventObject} e
45979              */
45980             "dblclick" : true,
45981             /**
45982              * @event contextmenu
45983              * The raw contextmenu event for the entire grid.
45984              * @param {Roo.EventObject} e
45985              */
45986             "contextmenu" : true,
45987             /**
45988              * @event mousedown
45989              * The raw mousedown event for the entire grid.
45990              * @param {Roo.EventObject} e
45991              */
45992             "mousedown" : true,
45993             /**
45994              * @event mouseup
45995              * The raw mouseup event for the entire grid.
45996              * @param {Roo.EventObject} e
45997              */
45998             "mouseup" : true,
45999             /**
46000              * @event mouseover
46001              * The raw mouseover event for the entire grid.
46002              * @param {Roo.EventObject} e
46003              */
46004             "mouseover" : true,
46005             /**
46006              * @event mouseout
46007              * The raw mouseout event for the entire grid.
46008              * @param {Roo.EventObject} e
46009              */
46010             "mouseout" : true,
46011             /**
46012              * @event keypress
46013              * The raw keypress event for the entire grid.
46014              * @param {Roo.EventObject} e
46015              */
46016             "keypress" : true,
46017             /**
46018              * @event keydown
46019              * The raw keydown event for the entire grid.
46020              * @param {Roo.EventObject} e
46021              */
46022             "keydown" : true,
46023
46024             // custom events
46025
46026             /**
46027              * @event cellclick
46028              * Fires when a cell is clicked
46029              * @param {Grid} this
46030              * @param {Number} rowIndex
46031              * @param {Number} columnIndex
46032              * @param {Roo.EventObject} e
46033              */
46034             "cellclick" : true,
46035             /**
46036              * @event celldblclick
46037              * Fires when a cell is double clicked
46038              * @param {Grid} this
46039              * @param {Number} rowIndex
46040              * @param {Number} columnIndex
46041              * @param {Roo.EventObject} e
46042              */
46043             "celldblclick" : true,
46044             /**
46045              * @event rowclick
46046              * Fires when a row is clicked
46047              * @param {Grid} this
46048              * @param {Number} rowIndex
46049              * @param {Roo.EventObject} e
46050              */
46051             "rowclick" : true,
46052             /**
46053              * @event rowdblclick
46054              * Fires when a row is double clicked
46055              * @param {Grid} this
46056              * @param {Number} rowIndex
46057              * @param {Roo.EventObject} e
46058              */
46059             "rowdblclick" : true,
46060             /**
46061              * @event headerclick
46062              * Fires when a header is clicked
46063              * @param {Grid} this
46064              * @param {Number} columnIndex
46065              * @param {Roo.EventObject} e
46066              */
46067             "headerclick" : true,
46068             /**
46069              * @event headerdblclick
46070              * Fires when a header cell is double clicked
46071              * @param {Grid} this
46072              * @param {Number} columnIndex
46073              * @param {Roo.EventObject} e
46074              */
46075             "headerdblclick" : true,
46076             /**
46077              * @event rowcontextmenu
46078              * Fires when a row is right clicked
46079              * @param {Grid} this
46080              * @param {Number} rowIndex
46081              * @param {Roo.EventObject} e
46082              */
46083             "rowcontextmenu" : true,
46084             /**
46085          * @event cellcontextmenu
46086          * Fires when a cell is right clicked
46087          * @param {Grid} this
46088          * @param {Number} rowIndex
46089          * @param {Number} cellIndex
46090          * @param {Roo.EventObject} e
46091          */
46092          "cellcontextmenu" : true,
46093             /**
46094              * @event headercontextmenu
46095              * Fires when a header is right clicked
46096              * @param {Grid} this
46097              * @param {Number} columnIndex
46098              * @param {Roo.EventObject} e
46099              */
46100             "headercontextmenu" : true,
46101             /**
46102              * @event bodyscroll
46103              * Fires when the body element is scrolled
46104              * @param {Number} scrollLeft
46105              * @param {Number} scrollTop
46106              */
46107             "bodyscroll" : true,
46108             /**
46109              * @event columnresize
46110              * Fires when the user resizes a column
46111              * @param {Number} columnIndex
46112              * @param {Number} newSize
46113              */
46114             "columnresize" : true,
46115             /**
46116              * @event columnmove
46117              * Fires when the user moves a column
46118              * @param {Number} oldIndex
46119              * @param {Number} newIndex
46120              */
46121             "columnmove" : true,
46122             /**
46123              * @event startdrag
46124              * Fires when row(s) start being dragged
46125              * @param {Grid} this
46126              * @param {Roo.GridDD} dd The drag drop object
46127              * @param {event} e The raw browser event
46128              */
46129             "startdrag" : true,
46130             /**
46131              * @event enddrag
46132              * Fires when a drag operation is complete
46133              * @param {Grid} this
46134              * @param {Roo.GridDD} dd The drag drop object
46135              * @param {event} e The raw browser event
46136              */
46137             "enddrag" : true,
46138             /**
46139              * @event dragdrop
46140              * Fires when dragged row(s) are dropped on a valid DD target
46141              * @param {Grid} this
46142              * @param {Roo.GridDD} dd The drag drop object
46143              * @param {String} targetId The target drag drop object
46144              * @param {event} e The raw browser event
46145              */
46146             "dragdrop" : true,
46147             /**
46148              * @event dragover
46149              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46150              * @param {Grid} this
46151              * @param {Roo.GridDD} dd The drag drop object
46152              * @param {String} targetId The target drag drop object
46153              * @param {event} e The raw browser event
46154              */
46155             "dragover" : true,
46156             /**
46157              * @event dragenter
46158              *  Fires when the dragged row(s) first cross another DD target while being dragged
46159              * @param {Grid} this
46160              * @param {Roo.GridDD} dd The drag drop object
46161              * @param {String} targetId The target drag drop object
46162              * @param {event} e The raw browser event
46163              */
46164             "dragenter" : true,
46165             /**
46166              * @event dragout
46167              * Fires when the dragged row(s) leave another DD target while being dragged
46168              * @param {Grid} this
46169              * @param {Roo.GridDD} dd The drag drop object
46170              * @param {String} targetId The target drag drop object
46171              * @param {event} e The raw browser event
46172              */
46173             "dragout" : true,
46174         /**
46175          * @event render
46176          * Fires when the grid is rendered
46177          * @param {Grid} grid
46178          */
46179         render : true
46180     });
46181
46182     Roo.grid.Grid.superclass.constructor.call(this);
46183 };
46184 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46185     
46186     /**
46187      * @cfg {String} ddGroup - drag drop group.
46188          */
46189     
46190     /**
46191      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46192          */
46193         minColumnWidth : 25,
46194
46195     /**
46196          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46197          * <b>on initial render.</b> It is more efficient to explicitly size the columns
46198          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46199          */
46200         autoSizeColumns : false,
46201
46202         /**
46203          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46204          */
46205         autoSizeHeaders : true,
46206
46207         /**
46208          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46209          */
46210         monitorWindowResize : true,
46211
46212         /**
46213          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46214          * rows measured to get a columns size. Default is 0 (all rows).
46215          */
46216         maxRowsToMeasure : 0,
46217
46218         /**
46219          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46220          */
46221         trackMouseOver : true,
46222
46223     /**
46224          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46225          */
46226     
46227         /**
46228          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46229          */
46230         enableDragDrop : false,
46231
46232         /**
46233          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46234          */
46235         enableColumnMove : true,
46236
46237         /**
46238          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46239          */
46240         enableColumnHide : true,
46241
46242         /**
46243          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46244          */
46245         enableRowHeightSync : false,
46246
46247         /**
46248          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46249          */
46250         stripeRows : true,
46251
46252         /**
46253          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46254          */
46255         autoHeight : false,
46256
46257     /**
46258      * @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.
46259      */
46260     autoExpandColumn : false,
46261
46262     /**
46263     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46264     * Default is 50.
46265     */
46266     autoExpandMin : 50,
46267
46268     /**
46269     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46270     */
46271     autoExpandMax : 1000,
46272
46273     /**
46274          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46275          */
46276         view : null,
46277
46278         /**
46279      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46280          */
46281         loadMask : false,
46282
46283     // private
46284     rendered : false,
46285
46286     /**
46287     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46288     * of a fixed width. Default is false.
46289     */
46290     /**
46291     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46292     */
46293     /**
46294      * Called once after all setup has been completed and the grid is ready to be rendered.
46295      * @return {Roo.grid.Grid} this
46296      */
46297     render : function(){
46298         var c = this.container;
46299         // try to detect autoHeight/width mode
46300         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46301             this.autoHeight = true;
46302         }
46303         var view = this.getView();
46304         view.init(this);
46305
46306         c.on("click", this.onClick, this);
46307         c.on("dblclick", this.onDblClick, this);
46308         c.on("contextmenu", this.onContextMenu, this);
46309         c.on("keydown", this.onKeyDown, this);
46310
46311         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46312
46313         this.getSelectionModel().init(this);
46314
46315         view.render();
46316
46317         if(this.loadMask){
46318             this.loadMask = new Roo.LoadMask(this.container,
46319                     Roo.apply({store:this.dataSource}, this.loadMask));
46320         }
46321         
46322         
46323         if (this.toolbar && this.toolbar.xtype) {
46324             this.toolbar.container = this.getView().getHeaderPanel(true);
46325             this.toolbar = new Ext.Toolbar(this.toolbar);
46326         }
46327         if (this.footer && this.footer.xtype) {
46328             this.footer.dataSource = this.getDataSource();
46329             this.footer.container = this.getView().getFooterPanel(true);
46330             this.footer = Roo.factory(this.footer, Roo);
46331         }
46332         this.rendered = true;
46333         this.fireEvent('render', this);
46334         return this;
46335     },
46336
46337         /**
46338          * Reconfigures the grid to use a different Store and Column Model.
46339          * The View will be bound to the new objects and refreshed.
46340          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46341          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46342          */
46343     reconfigure : function(dataSource, colModel){
46344         if(this.loadMask){
46345             this.loadMask.destroy();
46346             this.loadMask = new Roo.LoadMask(this.container,
46347                     Roo.apply({store:dataSource}, this.loadMask));
46348         }
46349         this.view.bind(dataSource, colModel);
46350         this.dataSource = dataSource;
46351         this.colModel = colModel;
46352         this.view.refresh(true);
46353     },
46354
46355     // private
46356     onKeyDown : function(e){
46357         this.fireEvent("keydown", e);
46358     },
46359
46360     /**
46361      * Destroy this grid.
46362      * @param {Boolean} removeEl True to remove the element
46363      */
46364     destroy : function(removeEl, keepListeners){
46365         if(this.loadMask){
46366             this.loadMask.destroy();
46367         }
46368         var c = this.container;
46369         c.removeAllListeners();
46370         this.view.destroy();
46371         this.colModel.purgeListeners();
46372         if(!keepListeners){
46373             this.purgeListeners();
46374         }
46375         c.update("");
46376         if(removeEl === true){
46377             c.remove();
46378         }
46379     },
46380
46381     // private
46382     processEvent : function(name, e){
46383         this.fireEvent(name, e);
46384         var t = e.getTarget();
46385         var v = this.view;
46386         var header = v.findHeaderIndex(t);
46387         if(header !== false){
46388             this.fireEvent("header" + name, this, header, e);
46389         }else{
46390             var row = v.findRowIndex(t);
46391             var cell = v.findCellIndex(t);
46392             if(row !== false){
46393                 this.fireEvent("row" + name, this, row, e);
46394                 if(cell !== false){
46395                     this.fireEvent("cell" + name, this, row, cell, e);
46396                 }
46397             }
46398         }
46399     },
46400
46401     // private
46402     onClick : function(e){
46403         this.processEvent("click", e);
46404     },
46405
46406     // private
46407     onContextMenu : function(e, t){
46408         this.processEvent("contextmenu", e);
46409     },
46410
46411     // private
46412     onDblClick : function(e){
46413         this.processEvent("dblclick", e);
46414     },
46415
46416     // private
46417     walkCells : function(row, col, step, fn, scope){
46418         var cm = this.colModel, clen = cm.getColumnCount();
46419         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46420         if(step < 0){
46421             if(col < 0){
46422                 row--;
46423                 first = false;
46424             }
46425             while(row >= 0){
46426                 if(!first){
46427                     col = clen-1;
46428                 }
46429                 first = false;
46430                 while(col >= 0){
46431                     if(fn.call(scope || this, row, col, cm) === true){
46432                         return [row, col];
46433                     }
46434                     col--;
46435                 }
46436                 row--;
46437             }
46438         } else {
46439             if(col >= clen){
46440                 row++;
46441                 first = false;
46442             }
46443             while(row < rlen){
46444                 if(!first){
46445                     col = 0;
46446                 }
46447                 first = false;
46448                 while(col < clen){
46449                     if(fn.call(scope || this, row, col, cm) === true){
46450                         return [row, col];
46451                     }
46452                     col++;
46453                 }
46454                 row++;
46455             }
46456         }
46457         return null;
46458     },
46459
46460     // private
46461     getSelections : function(){
46462         return this.selModel.getSelections();
46463     },
46464
46465     /**
46466      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
46467      * but if manual update is required this method will initiate it.
46468      */
46469     autoSize : function(){
46470         if(this.rendered){
46471             this.view.layout();
46472             if(this.view.adjustForScroll){
46473                 this.view.adjustForScroll();
46474             }
46475         }
46476     },
46477
46478     /**
46479      * Returns the grid's underlying element.
46480      * @return {Element} The element
46481      */
46482     getGridEl : function(){
46483         return this.container;
46484     },
46485
46486     // private for compatibility, overridden by editor grid
46487     stopEditing : function(){},
46488
46489     /**
46490      * Returns the grid's SelectionModel.
46491      * @return {SelectionModel}
46492      */
46493     getSelectionModel : function(){
46494         if(!this.selModel){
46495             this.selModel = new Roo.grid.RowSelectionModel();
46496         }
46497         return this.selModel;
46498     },
46499
46500     /**
46501      * Returns the grid's DataSource.
46502      * @return {DataSource}
46503      */
46504     getDataSource : function(){
46505         return this.dataSource;
46506     },
46507
46508     /**
46509      * Returns the grid's ColumnModel.
46510      * @return {ColumnModel}
46511      */
46512     getColumnModel : function(){
46513         return this.colModel;
46514     },
46515
46516     /**
46517      * Returns the grid's GridView object.
46518      * @return {GridView}
46519      */
46520     getView : function(){
46521         if(!this.view){
46522             this.view = new Roo.grid.GridView(this.viewConfig);
46523         }
46524         return this.view;
46525     },
46526     /**
46527      * Called to get grid's drag proxy text, by default returns this.ddText.
46528      * @return {String}
46529      */
46530     getDragDropText : function(){
46531         var count = this.selModel.getCount();
46532         return String.format(this.ddText, count, count == 1 ? '' : 's');
46533     }
46534 });
46535 /**
46536  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
46537  * %0 is replaced with the number of selected rows.
46538  * @type String
46539  */
46540 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
46541  * Based on:
46542  * Ext JS Library 1.1.1
46543  * Copyright(c) 2006-2007, Ext JS, LLC.
46544  *
46545  * Originally Released Under LGPL - original licence link has changed is not relivant.
46546  *
46547  * Fork - LGPL
46548  * <script type="text/javascript">
46549  */
46550  
46551 Roo.grid.AbstractGridView = function(){
46552         this.grid = null;
46553         
46554         this.events = {
46555             "beforerowremoved" : true,
46556             "beforerowsinserted" : true,
46557             "beforerefresh" : true,
46558             "rowremoved" : true,
46559             "rowsinserted" : true,
46560             "rowupdated" : true,
46561             "refresh" : true
46562         };
46563     Roo.grid.AbstractGridView.superclass.constructor.call(this);
46564 };
46565
46566 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
46567     rowClass : "x-grid-row",
46568     cellClass : "x-grid-cell",
46569     tdClass : "x-grid-td",
46570     hdClass : "x-grid-hd",
46571     splitClass : "x-grid-hd-split",
46572     
46573         init: function(grid){
46574         this.grid = grid;
46575                 var cid = this.grid.getGridEl().id;
46576         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
46577         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
46578         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
46579         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
46580         },
46581         
46582         getColumnRenderers : function(){
46583         var renderers = [];
46584         var cm = this.grid.colModel;
46585         var colCount = cm.getColumnCount();
46586         for(var i = 0; i < colCount; i++){
46587             renderers[i] = cm.getRenderer(i);
46588         }
46589         return renderers;
46590     },
46591     
46592     getColumnIds : function(){
46593         var ids = [];
46594         var cm = this.grid.colModel;
46595         var colCount = cm.getColumnCount();
46596         for(var i = 0; i < colCount; i++){
46597             ids[i] = cm.getColumnId(i);
46598         }
46599         return ids;
46600     },
46601     
46602     getDataIndexes : function(){
46603         if(!this.indexMap){
46604             this.indexMap = this.buildIndexMap();
46605         }
46606         return this.indexMap.colToData;
46607     },
46608     
46609     getColumnIndexByDataIndex : function(dataIndex){
46610         if(!this.indexMap){
46611             this.indexMap = this.buildIndexMap();
46612         }
46613         return this.indexMap.dataToCol[dataIndex];
46614     },
46615     
46616     /**
46617      * Set a css style for a column dynamically. 
46618      * @param {Number} colIndex The index of the column
46619      * @param {String} name The css property name
46620      * @param {String} value The css value
46621      */
46622     setCSSStyle : function(colIndex, name, value){
46623         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46624         Roo.util.CSS.updateRule(selector, name, value);
46625     },
46626     
46627     generateRules : function(cm){
46628         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46629         Roo.util.CSS.removeStyleSheet(rulesId);
46630         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46631             var cid = cm.getColumnId(i);
46632             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46633                          this.tdSelector, cid, " {\n}\n",
46634                          this.hdSelector, cid, " {\n}\n",
46635                          this.splitSelector, cid, " {\n}\n");
46636         }
46637         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46638     }
46639 });/*
46640  * Based on:
46641  * Ext JS Library 1.1.1
46642  * Copyright(c) 2006-2007, Ext JS, LLC.
46643  *
46644  * Originally Released Under LGPL - original licence link has changed is not relivant.
46645  *
46646  * Fork - LGPL
46647  * <script type="text/javascript">
46648  */
46649
46650 // private
46651 // This is a support class used internally by the Grid components
46652 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46653     this.grid = grid;
46654     this.view = grid.getView();
46655     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46656     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46657     if(hd2){
46658         this.setHandleElId(Roo.id(hd));
46659         this.setOuterHandleElId(Roo.id(hd2));
46660     }
46661     this.scroll = false;
46662 };
46663 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
46664     maxDragWidth: 120,
46665     getDragData : function(e){
46666         var t = Roo.lib.Event.getTarget(e);
46667         var h = this.view.findHeaderCell(t);
46668         if(h){
46669             return {ddel: h.firstChild, header:h};
46670         }
46671         return false;
46672     },
46673
46674     onInitDrag : function(e){
46675         this.view.headersDisabled = true;
46676         var clone = this.dragData.ddel.cloneNode(true);
46677         clone.id = Roo.id();
46678         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
46679         this.proxy.update(clone);
46680         return true;
46681     },
46682
46683     afterValidDrop : function(){
46684         var v = this.view;
46685         setTimeout(function(){
46686             v.headersDisabled = false;
46687         }, 50);
46688     },
46689
46690     afterInvalidDrop : function(){
46691         var v = this.view;
46692         setTimeout(function(){
46693             v.headersDisabled = false;
46694         }, 50);
46695     }
46696 });
46697 /*
46698  * Based on:
46699  * Ext JS Library 1.1.1
46700  * Copyright(c) 2006-2007, Ext JS, LLC.
46701  *
46702  * Originally Released Under LGPL - original licence link has changed is not relivant.
46703  *
46704  * Fork - LGPL
46705  * <script type="text/javascript">
46706  */
46707 // private
46708 // This is a support class used internally by the Grid components
46709 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46710     this.grid = grid;
46711     this.view = grid.getView();
46712     // split the proxies so they don't interfere with mouse events
46713     this.proxyTop = Roo.DomHelper.append(document.body, {
46714         cls:"col-move-top", html:"&#160;"
46715     }, true);
46716     this.proxyBottom = Roo.DomHelper.append(document.body, {
46717         cls:"col-move-bottom", html:"&#160;"
46718     }, true);
46719     this.proxyTop.hide = this.proxyBottom.hide = function(){
46720         this.setLeftTop(-100,-100);
46721         this.setStyle("visibility", "hidden");
46722     };
46723     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46724     // temporarily disabled
46725     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
46726     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
46727 };
46728 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
46729     proxyOffsets : [-4, -9],
46730     fly: Roo.Element.fly,
46731
46732     getTargetFromEvent : function(e){
46733         var t = Roo.lib.Event.getTarget(e);
46734         var cindex = this.view.findCellIndex(t);
46735         if(cindex !== false){
46736             return this.view.getHeaderCell(cindex);
46737         }
46738     },
46739
46740     nextVisible : function(h){
46741         var v = this.view, cm = this.grid.colModel;
46742         h = h.nextSibling;
46743         while(h){
46744             if(!cm.isHidden(v.getCellIndex(h))){
46745                 return h;
46746             }
46747             h = h.nextSibling;
46748         }
46749         return null;
46750     },
46751
46752     prevVisible : function(h){
46753         var v = this.view, cm = this.grid.colModel;
46754         h = h.prevSibling;
46755         while(h){
46756             if(!cm.isHidden(v.getCellIndex(h))){
46757                 return h;
46758             }
46759             h = h.prevSibling;
46760         }
46761         return null;
46762     },
46763
46764     positionIndicator : function(h, n, e){
46765         var x = Roo.lib.Event.getPageX(e);
46766         var r = Roo.lib.Dom.getRegion(n.firstChild);
46767         var px, pt, py = r.top + this.proxyOffsets[1];
46768         if((r.right - x) <= (r.right-r.left)/2){
46769             px = r.right+this.view.borderWidth;
46770             pt = "after";
46771         }else{
46772             px = r.left;
46773             pt = "before";
46774         }
46775         var oldIndex = this.view.getCellIndex(h);
46776         var newIndex = this.view.getCellIndex(n);
46777
46778         if(this.grid.colModel.isFixed(newIndex)){
46779             return false;
46780         }
46781
46782         var locked = this.grid.colModel.isLocked(newIndex);
46783
46784         if(pt == "after"){
46785             newIndex++;
46786         }
46787         if(oldIndex < newIndex){
46788             newIndex--;
46789         }
46790         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
46791             return false;
46792         }
46793         px +=  this.proxyOffsets[0];
46794         this.proxyTop.setLeftTop(px, py);
46795         this.proxyTop.show();
46796         if(!this.bottomOffset){
46797             this.bottomOffset = this.view.mainHd.getHeight();
46798         }
46799         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
46800         this.proxyBottom.show();
46801         return pt;
46802     },
46803
46804     onNodeEnter : function(n, dd, e, data){
46805         if(data.header != n){
46806             this.positionIndicator(data.header, n, e);
46807         }
46808     },
46809
46810     onNodeOver : function(n, dd, e, data){
46811         var result = false;
46812         if(data.header != n){
46813             result = this.positionIndicator(data.header, n, e);
46814         }
46815         if(!result){
46816             this.proxyTop.hide();
46817             this.proxyBottom.hide();
46818         }
46819         return result ? this.dropAllowed : this.dropNotAllowed;
46820     },
46821
46822     onNodeOut : function(n, dd, e, data){
46823         this.proxyTop.hide();
46824         this.proxyBottom.hide();
46825     },
46826
46827     onNodeDrop : function(n, dd, e, data){
46828         var h = data.header;
46829         if(h != n){
46830             var cm = this.grid.colModel;
46831             var x = Roo.lib.Event.getPageX(e);
46832             var r = Roo.lib.Dom.getRegion(n.firstChild);
46833             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
46834             var oldIndex = this.view.getCellIndex(h);
46835             var newIndex = this.view.getCellIndex(n);
46836             var locked = cm.isLocked(newIndex);
46837             if(pt == "after"){
46838                 newIndex++;
46839             }
46840             if(oldIndex < newIndex){
46841                 newIndex--;
46842             }
46843             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
46844                 return false;
46845             }
46846             cm.setLocked(oldIndex, locked, true);
46847             cm.moveColumn(oldIndex, newIndex);
46848             this.grid.fireEvent("columnmove", oldIndex, newIndex);
46849             return true;
46850         }
46851         return false;
46852     }
46853 });
46854 /*
46855  * Based on:
46856  * Ext JS Library 1.1.1
46857  * Copyright(c) 2006-2007, Ext JS, LLC.
46858  *
46859  * Originally Released Under LGPL - original licence link has changed is not relivant.
46860  *
46861  * Fork - LGPL
46862  * <script type="text/javascript">
46863  */
46864   
46865 /**
46866  * @class Roo.grid.GridView
46867  * @extends Roo.util.Observable
46868  *
46869  * @constructor
46870  * @param {Object} config
46871  */
46872 Roo.grid.GridView = function(config){
46873     Roo.grid.GridView.superclass.constructor.call(this);
46874     this.el = null;
46875
46876     Roo.apply(this, config);
46877 };
46878
46879 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
46880
46881     /**
46882      * Override this function to apply custom css classes to rows during rendering
46883      * @param {Record} record The record
46884      * @param {Number} index
46885      * @method getRowClass
46886      */
46887     rowClass : "x-grid-row",
46888
46889     cellClass : "x-grid-col",
46890
46891     tdClass : "x-grid-td",
46892
46893     hdClass : "x-grid-hd",
46894
46895     splitClass : "x-grid-split",
46896
46897     sortClasses : ["sort-asc", "sort-desc"],
46898
46899     enableMoveAnim : false,
46900
46901     hlColor: "C3DAF9",
46902
46903     dh : Roo.DomHelper,
46904
46905     fly : Roo.Element.fly,
46906
46907     css : Roo.util.CSS,
46908
46909     borderWidth: 1,
46910
46911     splitOffset: 3,
46912
46913     scrollIncrement : 22,
46914
46915     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
46916
46917     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
46918
46919     bind : function(ds, cm){
46920         if(this.ds){
46921             this.ds.un("load", this.onLoad, this);
46922             this.ds.un("datachanged", this.onDataChange, this);
46923             this.ds.un("add", this.onAdd, this);
46924             this.ds.un("remove", this.onRemove, this);
46925             this.ds.un("update", this.onUpdate, this);
46926             this.ds.un("clear", this.onClear, this);
46927         }
46928         if(ds){
46929             ds.on("load", this.onLoad, this);
46930             ds.on("datachanged", this.onDataChange, this);
46931             ds.on("add", this.onAdd, this);
46932             ds.on("remove", this.onRemove, this);
46933             ds.on("update", this.onUpdate, this);
46934             ds.on("clear", this.onClear, this);
46935         }
46936         this.ds = ds;
46937
46938         if(this.cm){
46939             this.cm.un("widthchange", this.onColWidthChange, this);
46940             this.cm.un("headerchange", this.onHeaderChange, this);
46941             this.cm.un("hiddenchange", this.onHiddenChange, this);
46942             this.cm.un("columnmoved", this.onColumnMove, this);
46943             this.cm.un("columnlockchange", this.onColumnLock, this);
46944         }
46945         if(cm){
46946             this.generateRules(cm);
46947             cm.on("widthchange", this.onColWidthChange, this);
46948             cm.on("headerchange", this.onHeaderChange, this);
46949             cm.on("hiddenchange", this.onHiddenChange, this);
46950             cm.on("columnmoved", this.onColumnMove, this);
46951             cm.on("columnlockchange", this.onColumnLock, this);
46952         }
46953         this.cm = cm;
46954     },
46955
46956     init: function(grid){
46957                 Roo.grid.GridView.superclass.init.call(this, grid);
46958
46959                 this.bind(grid.dataSource, grid.colModel);
46960
46961             grid.on("headerclick", this.handleHeaderClick, this);
46962
46963         if(grid.trackMouseOver){
46964             grid.on("mouseover", this.onRowOver, this);
46965                 grid.on("mouseout", this.onRowOut, this);
46966             }
46967             grid.cancelTextSelection = function(){};
46968                 this.gridId = grid.id;
46969
46970                 var tpls = this.templates || {};
46971
46972                 if(!tpls.master){
46973                     tpls.master = new Roo.Template(
46974                        '<div class="x-grid" hidefocus="true">',
46975                           '<div class="x-grid-topbar"></div>',
46976                           '<div class="x-grid-scroller"><div></div></div>',
46977                           '<div class="x-grid-locked">',
46978                               '<div class="x-grid-header">{lockedHeader}</div>',
46979                               '<div class="x-grid-body">{lockedBody}</div>',
46980                           "</div>",
46981                           '<div class="x-grid-viewport">',
46982                               '<div class="x-grid-header">{header}</div>',
46983                               '<div class="x-grid-body">{body}</div>',
46984                           "</div>",
46985                           '<div class="x-grid-bottombar"></div>',
46986                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46987                           '<div class="x-grid-resize-proxy">&#160;</div>',
46988                        "</div>"
46989                     );
46990                     tpls.master.disableformats = true;
46991                 }
46992
46993                 if(!tpls.header){
46994                     tpls.header = new Roo.Template(
46995                        '<table border="0" cellspacing="0" cellpadding="0">',
46996                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46997                        "</table>{splits}"
46998                     );
46999                     tpls.header.disableformats = true;
47000                 }
47001                 tpls.header.compile();
47002
47003                 if(!tpls.hcell){
47004                     tpls.hcell = new Roo.Template(
47005                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47006                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47007                         "</div></td>"
47008                      );
47009                      tpls.hcell.disableFormats = true;
47010                 }
47011                 tpls.hcell.compile();
47012
47013                 if(!tpls.hsplit){
47014                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47015                     tpls.hsplit.disableFormats = true;
47016                 }
47017                 tpls.hsplit.compile();
47018
47019                 if(!tpls.body){
47020                     tpls.body = new Roo.Template(
47021                        '<table border="0" cellspacing="0" cellpadding="0">',
47022                        "<tbody>{rows}</tbody>",
47023                        "</table>"
47024                     );
47025                     tpls.body.disableFormats = true;
47026                 }
47027                 tpls.body.compile();
47028
47029                 if(!tpls.row){
47030                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47031                     tpls.row.disableFormats = true;
47032                 }
47033                 tpls.row.compile();
47034
47035                 if(!tpls.cell){
47036                     tpls.cell = new Roo.Template(
47037                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47038                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47039                         "</td>"
47040                     );
47041             tpls.cell.disableFormats = true;
47042         }
47043                 tpls.cell.compile();
47044
47045                 this.templates = tpls;
47046         },
47047
47048         // remap these for backwards compat
47049     onColWidthChange : function(){
47050         this.updateColumns.apply(this, arguments);
47051     },
47052     onHeaderChange : function(){
47053         this.updateHeaders.apply(this, arguments);
47054     }, 
47055     onHiddenChange : function(){
47056         this.handleHiddenChange.apply(this, arguments);
47057     },
47058     onColumnMove : function(){
47059         this.handleColumnMove.apply(this, arguments);
47060     },
47061     onColumnLock : function(){
47062         this.handleLockChange.apply(this, arguments);
47063     },
47064
47065     onDataChange : function(){
47066         this.refresh();
47067         this.updateHeaderSortState();
47068     },
47069
47070         onClear : function(){
47071         this.refresh();
47072     },
47073
47074         onUpdate : function(ds, record){
47075         this.refreshRow(record);
47076     },
47077
47078     refreshRow : function(record){
47079         var ds = this.ds, index;
47080         if(typeof record == 'number'){
47081             index = record;
47082             record = ds.getAt(index);
47083         }else{
47084             index = ds.indexOf(record);
47085         }
47086         this.insertRows(ds, index, index, true);
47087         this.onRemove(ds, record, index+1, true);
47088         this.syncRowHeights(index, index);
47089         this.layout();
47090         this.fireEvent("rowupdated", this, index, record);
47091     },
47092
47093     onAdd : function(ds, records, index){
47094         this.insertRows(ds, index, index + (records.length-1));
47095     },
47096
47097     onRemove : function(ds, record, index, isUpdate){
47098         if(isUpdate !== true){
47099             this.fireEvent("beforerowremoved", this, index, record);
47100         }
47101         var bt = this.getBodyTable(), lt = this.getLockedTable();
47102         if(bt.rows[index]){
47103             bt.firstChild.removeChild(bt.rows[index]);
47104         }
47105         if(lt.rows[index]){
47106             lt.firstChild.removeChild(lt.rows[index]);
47107         }
47108         if(isUpdate !== true){
47109             this.stripeRows(index);
47110             this.syncRowHeights(index, index);
47111             this.layout();
47112             this.fireEvent("rowremoved", this, index, record);
47113         }
47114     },
47115
47116     onLoad : function(){
47117         this.scrollToTop();
47118     },
47119
47120     /**
47121      * Scrolls the grid to the top
47122      */
47123     scrollToTop : function(){
47124         if(this.scroller){
47125             this.scroller.dom.scrollTop = 0;
47126             this.syncScroll();
47127         }
47128     },
47129
47130     /**
47131      * Gets a panel in the header of the grid that can be used for toolbars etc.
47132      * After modifying the contents of this panel a call to grid.autoSize() may be
47133      * required to register any changes in size.
47134      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47135      * @return Roo.Element
47136      */
47137     getHeaderPanel : function(doShow){
47138         if(doShow){
47139             this.headerPanel.show();
47140         }
47141         return this.headerPanel;
47142         },
47143
47144         /**
47145      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47146      * After modifying the contents of this panel a call to grid.autoSize() may be
47147      * required to register any changes in size.
47148      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47149      * @return Roo.Element
47150      */
47151     getFooterPanel : function(doShow){
47152         if(doShow){
47153             this.footerPanel.show();
47154         }
47155         return this.footerPanel;
47156         },
47157
47158         initElements : function(){
47159             var E = Roo.Element;
47160             var el = this.grid.getGridEl().dom.firstChild;
47161             var cs = el.childNodes;
47162
47163             this.el = new E(el);
47164             this.headerPanel = new E(el.firstChild);
47165             this.headerPanel.enableDisplayMode("block");
47166
47167         this.scroller = new E(cs[1]);
47168             this.scrollSizer = new E(this.scroller.dom.firstChild);
47169
47170             this.lockedWrap = new E(cs[2]);
47171             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47172             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47173
47174             this.mainWrap = new E(cs[3]);
47175             this.mainHd = new E(this.mainWrap.dom.firstChild);
47176             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47177
47178             this.footerPanel = new E(cs[4]);
47179             this.footerPanel.enableDisplayMode("block");
47180
47181         this.focusEl = new E(cs[5]);
47182         this.focusEl.swallowEvent("click", true);
47183         this.resizeProxy = new E(cs[6]);
47184
47185             this.headerSelector = String.format(
47186                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47187                this.lockedHd.id, this.mainHd.id
47188             );
47189
47190             this.splitterSelector = String.format(
47191                '#{0} div.x-grid-split, #{1} div.x-grid-split',
47192                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47193             );
47194     },
47195     idToCssName : function(s)
47196     {
47197         return s.replace(/[^a-z0-9]+/ig, '-');
47198     },
47199
47200         getHeaderCell : function(index){
47201             return Roo.DomQuery.select(this.headerSelector)[index];
47202         },
47203
47204         getHeaderCellMeasure : function(index){
47205             return this.getHeaderCell(index).firstChild;
47206         },
47207
47208         getHeaderCellText : function(index){
47209             return this.getHeaderCell(index).firstChild.firstChild;
47210         },
47211
47212         getLockedTable : function(){
47213             return this.lockedBody.dom.firstChild;
47214         },
47215
47216         getBodyTable : function(){
47217             return this.mainBody.dom.firstChild;
47218         },
47219
47220         getLockedRow : function(index){
47221             return this.getLockedTable().rows[index];
47222         },
47223
47224         getRow : function(index){
47225             return this.getBodyTable().rows[index];
47226         },
47227
47228         getRowComposite : function(index){
47229             if(!this.rowEl){
47230                 this.rowEl = new Roo.CompositeElementLite();
47231             }
47232         var els = [], lrow, mrow;
47233         if(lrow = this.getLockedRow(index)){
47234             els.push(lrow);
47235         }
47236         if(mrow = this.getRow(index)){
47237             els.push(mrow);
47238         }
47239         this.rowEl.elements = els;
47240             return this.rowEl;
47241         },
47242
47243         getCell : function(rowIndex, colIndex){
47244             var locked = this.cm.getLockedCount();
47245             var source;
47246             if(colIndex < locked){
47247                 source = this.lockedBody.dom.firstChild;
47248             }else{
47249                 source = this.mainBody.dom.firstChild;
47250                 colIndex -= locked;
47251             }
47252         return source.rows[rowIndex].childNodes[colIndex];
47253         },
47254
47255         getCellText : function(rowIndex, colIndex){
47256             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47257         },
47258
47259         getCellBox : function(cell){
47260             var b = this.fly(cell).getBox();
47261         if(Roo.isOpera){ // opera fails to report the Y
47262             b.y = cell.offsetTop + this.mainBody.getY();
47263         }
47264         return b;
47265     },
47266
47267     getCellIndex : function(cell){
47268         var id = String(cell.className).match(this.cellRE);
47269         if(id){
47270             return parseInt(id[1], 10);
47271         }
47272         return 0;
47273     },
47274
47275     findHeaderIndex : function(n){
47276         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47277         return r ? this.getCellIndex(r) : false;
47278     },
47279
47280     findHeaderCell : function(n){
47281         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47282         return r ? r : false;
47283     },
47284
47285     findRowIndex : function(n){
47286         if(!n){
47287             return false;
47288         }
47289         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47290         return r ? r.rowIndex : false;
47291     },
47292
47293     findCellIndex : function(node){
47294         var stop = this.el.dom;
47295         while(node && node != stop){
47296             if(this.findRE.test(node.className)){
47297                 return this.getCellIndex(node);
47298             }
47299             node = node.parentNode;
47300         }
47301         return false;
47302     },
47303
47304     getColumnId : function(index){
47305             return this.cm.getColumnId(index);
47306         },
47307
47308         getSplitters : function(){
47309             if(this.splitterSelector){
47310                return Roo.DomQuery.select(this.splitterSelector);
47311             }else{
47312                 return null;
47313             }
47314         },
47315
47316         getSplitter : function(index){
47317             return this.getSplitters()[index];
47318         },
47319
47320     onRowOver : function(e, t){
47321         var row;
47322         if((row = this.findRowIndex(t)) !== false){
47323             this.getRowComposite(row).addClass("x-grid-row-over");
47324         }
47325     },
47326
47327     onRowOut : function(e, t){
47328         var row;
47329         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47330             this.getRowComposite(row).removeClass("x-grid-row-over");
47331         }
47332     },
47333
47334     renderHeaders : function(){
47335             var cm = this.cm;
47336         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47337         var cb = [], lb = [], sb = [], lsb = [], p = {};
47338         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47339             p.cellId = "x-grid-hd-0-" + i;
47340             p.splitId = "x-grid-csplit-0-" + i;
47341             p.id = cm.getColumnId(i);
47342             p.title = cm.getColumnTooltip(i) || "";
47343             p.value = cm.getColumnHeader(i) || "";
47344             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47345             if(!cm.isLocked(i)){
47346                 cb[cb.length] = ct.apply(p);
47347                 sb[sb.length] = st.apply(p);
47348             }else{
47349                 lb[lb.length] = ct.apply(p);
47350                 lsb[lsb.length] = st.apply(p);
47351             }
47352         }
47353         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47354                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47355         },
47356
47357         updateHeaders : function(){
47358         var html = this.renderHeaders();
47359         this.lockedHd.update(html[0]);
47360         this.mainHd.update(html[1]);
47361     },
47362
47363     /**
47364      * Focuses the specified row.
47365      * @param {Number} row The row index
47366      */
47367     focusRow : function(row){
47368         var x = this.scroller.dom.scrollLeft;
47369         this.focusCell(row, 0, false);
47370         this.scroller.dom.scrollLeft = x;
47371     },
47372
47373     /**
47374      * Focuses the specified cell.
47375      * @param {Number} row The row index
47376      * @param {Number} col The column index
47377      * @param {Boolean} hscroll false to disable horizontal scrolling
47378      */
47379     focusCell : function(row, col, hscroll){
47380         var el = this.ensureVisible(row, col, hscroll);
47381         this.focusEl.alignTo(el, "tl-tl");
47382         if(Roo.isGecko){
47383             this.focusEl.focus();
47384         }else{
47385             this.focusEl.focus.defer(1, this.focusEl);
47386         }
47387     },
47388
47389     /**
47390      * Scrolls the specified cell into view
47391      * @param {Number} row The row index
47392      * @param {Number} col The column index
47393      * @param {Boolean} hscroll false to disable horizontal scrolling
47394      */
47395     ensureVisible : function(row, col, hscroll){
47396         if(typeof row != "number"){
47397             row = row.rowIndex;
47398         }
47399         if(row < 0 && row >= this.ds.getCount()){
47400             return;
47401         }
47402         col = (col !== undefined ? col : 0);
47403         var cm = this.grid.colModel;
47404         while(cm.isHidden(col)){
47405             col++;
47406         }
47407
47408         var el = this.getCell(row, col);
47409         if(!el){
47410             return;
47411         }
47412         var c = this.scroller.dom;
47413
47414         var ctop = parseInt(el.offsetTop, 10);
47415         var cleft = parseInt(el.offsetLeft, 10);
47416         var cbot = ctop + el.offsetHeight;
47417         var cright = cleft + el.offsetWidth;
47418
47419         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47420         var stop = parseInt(c.scrollTop, 10);
47421         var sleft = parseInt(c.scrollLeft, 10);
47422         var sbot = stop + ch;
47423         var sright = sleft + c.clientWidth;
47424
47425         if(ctop < stop){
47426                 c.scrollTop = ctop;
47427         }else if(cbot > sbot){
47428             c.scrollTop = cbot-ch;
47429         }
47430
47431         if(hscroll !== false){
47432             if(cleft < sleft){
47433                 c.scrollLeft = cleft;
47434             }else if(cright > sright){
47435                 c.scrollLeft = cright-c.clientWidth;
47436             }
47437         }
47438         return el;
47439     },
47440
47441     updateColumns : function(){
47442         this.grid.stopEditing();
47443         var cm = this.grid.colModel, colIds = this.getColumnIds();
47444         //var totalWidth = cm.getTotalWidth();
47445         var pos = 0;
47446         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47447             //if(cm.isHidden(i)) continue;
47448             var w = cm.getColumnWidth(i);
47449             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47450             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47451         }
47452         this.updateSplitters();
47453     },
47454
47455     generateRules : function(cm){
47456         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
47457         Roo.util.CSS.removeStyleSheet(rulesId);
47458         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47459             var cid = cm.getColumnId(i);
47460             var align = '';
47461             if(cm.config[i].align){
47462                 align = 'text-align:'+cm.config[i].align+';';
47463             }
47464             var hidden = '';
47465             if(cm.isHidden(i)){
47466                 hidden = 'display:none;';
47467             }
47468             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
47469             ruleBuf.push(
47470                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
47471                     this.hdSelector, cid, " {\n", align, width, "}\n",
47472                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
47473                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
47474         }
47475         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47476     },
47477
47478     updateSplitters : function(){
47479         var cm = this.cm, s = this.getSplitters();
47480         if(s){ // splitters not created yet
47481             var pos = 0, locked = true;
47482             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47483                 if(cm.isHidden(i)) continue;
47484                 var w = cm.getColumnWidth(i);
47485                 if(!cm.isLocked(i) && locked){
47486                     pos = 0;
47487                     locked = false;
47488                 }
47489                 pos += w;
47490                 s[i].style.left = (pos-this.splitOffset) + "px";
47491             }
47492         }
47493     },
47494
47495     handleHiddenChange : function(colModel, colIndex, hidden){
47496         if(hidden){
47497             this.hideColumn(colIndex);
47498         }else{
47499             this.unhideColumn(colIndex);
47500         }
47501     },
47502
47503     hideColumn : function(colIndex){
47504         var cid = this.getColumnId(colIndex);
47505         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
47506         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
47507         if(Roo.isSafari){
47508             this.updateHeaders();
47509         }
47510         this.updateSplitters();
47511         this.layout();
47512     },
47513
47514     unhideColumn : function(colIndex){
47515         var cid = this.getColumnId(colIndex);
47516         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
47517         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
47518
47519         if(Roo.isSafari){
47520             this.updateHeaders();
47521         }
47522         this.updateSplitters();
47523         this.layout();
47524     },
47525
47526     insertRows : function(dm, firstRow, lastRow, isUpdate){
47527         if(firstRow == 0 && lastRow == dm.getCount()-1){
47528             this.refresh();
47529         }else{
47530             if(!isUpdate){
47531                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
47532             }
47533             var s = this.getScrollState();
47534             var markup = this.renderRows(firstRow, lastRow);
47535             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
47536             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
47537             this.restoreScroll(s);
47538             if(!isUpdate){
47539                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
47540                 this.syncRowHeights(firstRow, lastRow);
47541                 this.stripeRows(firstRow);
47542                 this.layout();
47543             }
47544         }
47545     },
47546
47547     bufferRows : function(markup, target, index){
47548         var before = null, trows = target.rows, tbody = target.tBodies[0];
47549         if(index < trows.length){
47550             before = trows[index];
47551         }
47552         var b = document.createElement("div");
47553         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
47554         var rows = b.firstChild.rows;
47555         for(var i = 0, len = rows.length; i < len; i++){
47556             if(before){
47557                 tbody.insertBefore(rows[0], before);
47558             }else{
47559                 tbody.appendChild(rows[0]);
47560             }
47561         }
47562         b.innerHTML = "";
47563         b = null;
47564     },
47565
47566     deleteRows : function(dm, firstRow, lastRow){
47567         if(dm.getRowCount()<1){
47568             this.fireEvent("beforerefresh", this);
47569             this.mainBody.update("");
47570             this.lockedBody.update("");
47571             this.fireEvent("refresh", this);
47572         }else{
47573             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
47574             var bt = this.getBodyTable();
47575             var tbody = bt.firstChild;
47576             var rows = bt.rows;
47577             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
47578                 tbody.removeChild(rows[firstRow]);
47579             }
47580             this.stripeRows(firstRow);
47581             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
47582         }
47583     },
47584
47585     updateRows : function(dataSource, firstRow, lastRow){
47586         var s = this.getScrollState();
47587         this.refresh();
47588         this.restoreScroll(s);
47589     },
47590
47591     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
47592         if(!noRefresh){
47593            this.refresh();
47594         }
47595         this.updateHeaderSortState();
47596     },
47597
47598     getScrollState : function(){
47599         var sb = this.scroller.dom;
47600         return {left: sb.scrollLeft, top: sb.scrollTop};
47601     },
47602
47603     stripeRows : function(startRow){
47604         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47605             return;
47606         }
47607         startRow = startRow || 0;
47608         var rows = this.getBodyTable().rows;
47609         var lrows = this.getLockedTable().rows;
47610         var cls = ' x-grid-row-alt ';
47611         for(var i = startRow, len = rows.length; i < len; i++){
47612             var row = rows[i], lrow = lrows[i];
47613             var isAlt = ((i+1) % 2 == 0);
47614             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47615             if(isAlt == hasAlt){
47616                 continue;
47617             }
47618             if(isAlt){
47619                 row.className += " x-grid-row-alt";
47620             }else{
47621                 row.className = row.className.replace("x-grid-row-alt", "");
47622             }
47623             if(lrow){
47624                 lrow.className = row.className;
47625             }
47626         }
47627     },
47628
47629     restoreScroll : function(state){
47630         var sb = this.scroller.dom;
47631         sb.scrollLeft = state.left;
47632         sb.scrollTop = state.top;
47633         this.syncScroll();
47634     },
47635
47636     syncScroll : function(){
47637         var sb = this.scroller.dom;
47638         var sh = this.mainHd.dom;
47639         var bs = this.mainBody.dom;
47640         var lv = this.lockedBody.dom;
47641         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
47642         lv.scrollTop = bs.scrollTop = sb.scrollTop;
47643     },
47644
47645     handleScroll : function(e){
47646         this.syncScroll();
47647         var sb = this.scroller.dom;
47648         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
47649         e.stopEvent();
47650     },
47651
47652     handleWheel : function(e){
47653         var d = e.getWheelDelta();
47654         this.scroller.dom.scrollTop -= d*22;
47655         // set this here to prevent jumpy scrolling on large tables
47656         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
47657         e.stopEvent();
47658     },
47659
47660     renderRows : function(startRow, endRow){
47661         // pull in all the crap needed to render rows
47662         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
47663         var colCount = cm.getColumnCount();
47664
47665         if(ds.getCount() < 1){
47666             return ["", ""];
47667         }
47668
47669         // build a map for all the columns
47670         var cs = [];
47671         for(var i = 0; i < colCount; i++){
47672             var name = cm.getDataIndex(i);
47673             cs[i] = {
47674                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
47675                 renderer : cm.getRenderer(i),
47676                 id : cm.getColumnId(i),
47677                 locked : cm.isLocked(i)
47678             };
47679         }
47680
47681         startRow = startRow || 0;
47682         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
47683
47684         // records to render
47685         var rs = ds.getRange(startRow, endRow);
47686
47687         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
47688     },
47689
47690     // As much as I hate to duplicate code, this was branched because FireFox really hates
47691     // [].join("") on strings. The performance difference was substantial enough to
47692     // branch this function
47693     doRender : Roo.isGecko ?
47694             function(cs, rs, ds, startRow, colCount, stripe){
47695                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47696                 // buffers
47697                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47698                 for(var j = 0, len = rs.length; j < len; j++){
47699                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
47700                     for(var i = 0; i < colCount; i++){
47701                         c = cs[i];
47702                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47703                         p.id = c.id;
47704                         p.css = p.attr = "";
47705                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47706                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47707                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47708                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47709                         }
47710                         var markup = ct.apply(p);
47711                         if(!c.locked){
47712                             cb+= markup;
47713                         }else{
47714                             lcb+= markup;
47715                         }
47716                     }
47717                     var alt = [];
47718                     if(stripe && ((rowIndex+1) % 2 == 0)){
47719                         alt[0] = "x-grid-row-alt";
47720                     }
47721                     if(r.dirty){
47722                         alt[1] = " x-grid-dirty-row";
47723                     }
47724                     rp.cells = lcb;
47725                     if(this.getRowClass){
47726                         alt[2] = this.getRowClass(r, rowIndex);
47727                     }
47728                     rp.alt = alt.join(" ");
47729                     lbuf+= rt.apply(rp);
47730                     rp.cells = cb;
47731                     buf+=  rt.apply(rp);
47732                 }
47733                 return [lbuf, buf];
47734             } :
47735             function(cs, rs, ds, startRow, colCount, stripe){
47736                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47737                 // buffers
47738                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47739                 for(var j = 0, len = rs.length; j < len; j++){
47740                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47741                     for(var i = 0; i < colCount; i++){
47742                         c = cs[i];
47743                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47744                         p.id = c.id;
47745                         p.css = p.attr = "";
47746                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47747                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47748                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47749                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47750                         }
47751                         var markup = ct.apply(p);
47752                         if(!c.locked){
47753                             cb[cb.length] = markup;
47754                         }else{
47755                             lcb[lcb.length] = markup;
47756                         }
47757                     }
47758                     var alt = [];
47759                     if(stripe && ((rowIndex+1) % 2 == 0)){
47760                         alt[0] = "x-grid-row-alt";
47761                     }
47762                     if(r.dirty){
47763                         alt[1] = " x-grid-dirty-row";
47764                     }
47765                     rp.cells = lcb;
47766                     if(this.getRowClass){
47767                         alt[2] = this.getRowClass(r, rowIndex);
47768                     }
47769                     rp.alt = alt.join(" ");
47770                     rp.cells = lcb.join("");
47771                     lbuf[lbuf.length] = rt.apply(rp);
47772                     rp.cells = cb.join("");
47773                     buf[buf.length] =  rt.apply(rp);
47774                 }
47775                 return [lbuf.join(""), buf.join("")];
47776             },
47777
47778     renderBody : function(){
47779         var markup = this.renderRows();
47780         var bt = this.templates.body;
47781         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
47782     },
47783
47784     /**
47785      * Refreshes the grid
47786      * @param {Boolean} headersToo
47787      */
47788     refresh : function(headersToo){
47789         this.fireEvent("beforerefresh", this);
47790         this.grid.stopEditing();
47791         var result = this.renderBody();
47792         this.lockedBody.update(result[0]);
47793         this.mainBody.update(result[1]);
47794         if(headersToo === true){
47795             this.updateHeaders();
47796             this.updateColumns();
47797             this.updateSplitters();
47798             this.updateHeaderSortState();
47799         }
47800         this.syncRowHeights();
47801         this.layout();
47802         this.fireEvent("refresh", this);
47803     },
47804
47805     handleColumnMove : function(cm, oldIndex, newIndex){
47806         this.indexMap = null;
47807         var s = this.getScrollState();
47808         this.refresh(true);
47809         this.restoreScroll(s);
47810         this.afterMove(newIndex);
47811     },
47812
47813     afterMove : function(colIndex){
47814         if(this.enableMoveAnim && Roo.enableFx){
47815             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
47816         }
47817     },
47818
47819     updateCell : function(dm, rowIndex, dataIndex){
47820         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
47821         if(typeof colIndex == "undefined"){ // not present in grid
47822             return;
47823         }
47824         var cm = this.grid.colModel;
47825         var cell = this.getCell(rowIndex, colIndex);
47826         var cellText = this.getCellText(rowIndex, colIndex);
47827
47828         var p = {
47829             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
47830             id : cm.getColumnId(colIndex),
47831             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
47832         };
47833         var renderer = cm.getRenderer(colIndex);
47834         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
47835         if(typeof val == "undefined" || val === "") val = "&#160;";
47836         cellText.innerHTML = val;
47837         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
47838         this.syncRowHeights(rowIndex, rowIndex);
47839     },
47840
47841     calcColumnWidth : function(colIndex, maxRowsToMeasure){
47842         var maxWidth = 0;
47843         if(this.grid.autoSizeHeaders){
47844             var h = this.getHeaderCellMeasure(colIndex);
47845             maxWidth = Math.max(maxWidth, h.scrollWidth);
47846         }
47847         var tb, index;
47848         if(this.cm.isLocked(colIndex)){
47849             tb = this.getLockedTable();
47850             index = colIndex;
47851         }else{
47852             tb = this.getBodyTable();
47853             index = colIndex - this.cm.getLockedCount();
47854         }
47855         if(tb && tb.rows){
47856             var rows = tb.rows;
47857             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
47858             for(var i = 0; i < stopIndex; i++){
47859                 var cell = rows[i].childNodes[index].firstChild;
47860                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
47861             }
47862         }
47863         return maxWidth + /*margin for error in IE*/ 5;
47864     },
47865     /**
47866      * Autofit a column to its content.
47867      * @param {Number} colIndex
47868      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
47869      */
47870      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
47871          if(this.cm.isHidden(colIndex)){
47872              return; // can't calc a hidden column
47873          }
47874         if(forceMinSize){
47875             var cid = this.cm.getColumnId(colIndex);
47876             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
47877            if(this.grid.autoSizeHeaders){
47878                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
47879            }
47880         }
47881         var newWidth = this.calcColumnWidth(colIndex);
47882         this.cm.setColumnWidth(colIndex,
47883             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
47884         if(!suppressEvent){
47885             this.grid.fireEvent("columnresize", colIndex, newWidth);
47886         }
47887     },
47888
47889     /**
47890      * Autofits all columns to their content and then expands to fit any extra space in the grid
47891      */
47892      autoSizeColumns : function(){
47893         var cm = this.grid.colModel;
47894         var colCount = cm.getColumnCount();
47895         for(var i = 0; i < colCount; i++){
47896             this.autoSizeColumn(i, true, true);
47897         }
47898         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
47899             this.fitColumns();
47900         }else{
47901             this.updateColumns();
47902             this.layout();
47903         }
47904     },
47905
47906     /**
47907      * Autofits all columns to the grid's width proportionate with their current size
47908      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
47909      */
47910     fitColumns : function(reserveScrollSpace){
47911         var cm = this.grid.colModel;
47912         var colCount = cm.getColumnCount();
47913         var cols = [];
47914         var width = 0;
47915         var i, w;
47916         for (i = 0; i < colCount; i++){
47917             if(!cm.isHidden(i) && !cm.isFixed(i)){
47918                 w = cm.getColumnWidth(i);
47919                 cols.push(i);
47920                 cols.push(w);
47921                 width += w;
47922             }
47923         }
47924         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
47925         if(reserveScrollSpace){
47926             avail -= 17;
47927         }
47928         var frac = (avail - cm.getTotalWidth())/width;
47929         while (cols.length){
47930             w = cols.pop();
47931             i = cols.pop();
47932             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
47933         }
47934         this.updateColumns();
47935         this.layout();
47936     },
47937
47938     onRowSelect : function(rowIndex){
47939         var row = this.getRowComposite(rowIndex);
47940         row.addClass("x-grid-row-selected");
47941     },
47942
47943     onRowDeselect : function(rowIndex){
47944         var row = this.getRowComposite(rowIndex);
47945         row.removeClass("x-grid-row-selected");
47946     },
47947
47948     onCellSelect : function(row, col){
47949         var cell = this.getCell(row, col);
47950         if(cell){
47951             Roo.fly(cell).addClass("x-grid-cell-selected");
47952         }
47953     },
47954
47955     onCellDeselect : function(row, col){
47956         var cell = this.getCell(row, col);
47957         if(cell){
47958             Roo.fly(cell).removeClass("x-grid-cell-selected");
47959         }
47960     },
47961
47962     updateHeaderSortState : function(){
47963         var state = this.ds.getSortState();
47964         if(!state){
47965             return;
47966         }
47967         this.sortState = state;
47968         var sortColumn = this.cm.findColumnIndex(state.field);
47969         if(sortColumn != -1){
47970             var sortDir = state.direction;
47971             var sc = this.sortClasses;
47972             var hds = this.el.select(this.headerSelector).removeClass(sc);
47973             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47974         }
47975     },
47976
47977     handleHeaderClick : function(g, index){
47978         if(this.headersDisabled){
47979             return;
47980         }
47981         var dm = g.dataSource, cm = g.colModel;
47982             if(!cm.isSortable(index)){
47983             return;
47984         }
47985             g.stopEditing();
47986         dm.sort(cm.getDataIndex(index));
47987     },
47988
47989
47990     destroy : function(){
47991         if(this.colMenu){
47992             this.colMenu.removeAll();
47993             Roo.menu.MenuMgr.unregister(this.colMenu);
47994             this.colMenu.getEl().remove();
47995             delete this.colMenu;
47996         }
47997         if(this.hmenu){
47998             this.hmenu.removeAll();
47999             Roo.menu.MenuMgr.unregister(this.hmenu);
48000             this.hmenu.getEl().remove();
48001             delete this.hmenu;
48002         }
48003         if(this.grid.enableColumnMove){
48004             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48005             if(dds){
48006                 for(var dd in dds){
48007                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48008                         var elid = dds[dd].dragElId;
48009                         dds[dd].unreg();
48010                         Roo.get(elid).remove();
48011                     } else if(dds[dd].config.isTarget){
48012                         dds[dd].proxyTop.remove();
48013                         dds[dd].proxyBottom.remove();
48014                         dds[dd].unreg();
48015                     }
48016                     if(Roo.dd.DDM.locationCache[dd]){
48017                         delete Roo.dd.DDM.locationCache[dd];
48018                     }
48019                 }
48020                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48021             }
48022         }
48023         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48024         this.bind(null, null);
48025         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48026     },
48027
48028     handleLockChange : function(){
48029         this.refresh(true);
48030     },
48031
48032     onDenyColumnLock : function(){
48033
48034     },
48035
48036     onDenyColumnHide : function(){
48037
48038     },
48039
48040     handleHdMenuClick : function(item){
48041         var index = this.hdCtxIndex;
48042         var cm = this.cm, ds = this.ds;
48043         switch(item.id){
48044             case "asc":
48045                 ds.sort(cm.getDataIndex(index), "ASC");
48046                 break;
48047             case "desc":
48048                 ds.sort(cm.getDataIndex(index), "DESC");
48049                 break;
48050             case "lock":
48051                 var lc = cm.getLockedCount();
48052                 if(cm.getColumnCount(true) <= lc+1){
48053                     this.onDenyColumnLock();
48054                     return;
48055                 }
48056                 if(lc != index){
48057                     cm.setLocked(index, true, true);
48058                     cm.moveColumn(index, lc);
48059                     this.grid.fireEvent("columnmove", index, lc);
48060                 }else{
48061                     cm.setLocked(index, true);
48062                 }
48063             break;
48064             case "unlock":
48065                 var lc = cm.getLockedCount();
48066                 if((lc-1) != index){
48067                     cm.setLocked(index, false, true);
48068                     cm.moveColumn(index, lc-1);
48069                     this.grid.fireEvent("columnmove", index, lc-1);
48070                 }else{
48071                     cm.setLocked(index, false);
48072                 }
48073             break;
48074             default:
48075                 index = cm.getIndexById(item.id.substr(4));
48076                 if(index != -1){
48077                     if(item.checked && cm.getColumnCount(true) <= 1){
48078                         this.onDenyColumnHide();
48079                         return false;
48080                     }
48081                     cm.setHidden(index, item.checked);
48082                 }
48083         }
48084         return true;
48085     },
48086
48087     beforeColMenuShow : function(){
48088         var cm = this.cm,  colCount = cm.getColumnCount();
48089         this.colMenu.removeAll();
48090         for(var i = 0; i < colCount; i++){
48091             this.colMenu.add(new Roo.menu.CheckItem({
48092                 id: "col-"+cm.getColumnId(i),
48093                 text: cm.getColumnHeader(i),
48094                 checked: !cm.isHidden(i),
48095                 hideOnClick:false
48096             }));
48097         }
48098     },
48099
48100     handleHdCtx : function(g, index, e){
48101         e.stopEvent();
48102         var hd = this.getHeaderCell(index);
48103         this.hdCtxIndex = index;
48104         var ms = this.hmenu.items, cm = this.cm;
48105         ms.get("asc").setDisabled(!cm.isSortable(index));
48106         ms.get("desc").setDisabled(!cm.isSortable(index));
48107         if(this.grid.enableColLock !== false){
48108             ms.get("lock").setDisabled(cm.isLocked(index));
48109             ms.get("unlock").setDisabled(!cm.isLocked(index));
48110         }
48111         this.hmenu.show(hd, "tl-bl");
48112     },
48113
48114     handleHdOver : function(e){
48115         var hd = this.findHeaderCell(e.getTarget());
48116         if(hd && !this.headersDisabled){
48117             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48118                this.fly(hd).addClass("x-grid-hd-over");
48119             }
48120         }
48121     },
48122
48123     handleHdOut : function(e){
48124         var hd = this.findHeaderCell(e.getTarget());
48125         if(hd){
48126             this.fly(hd).removeClass("x-grid-hd-over");
48127         }
48128     },
48129
48130     handleSplitDblClick : function(e, t){
48131         var i = this.getCellIndex(t);
48132         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48133             this.autoSizeColumn(i, true);
48134             this.layout();
48135         }
48136     },
48137
48138     render : function(){
48139
48140         var cm = this.cm;
48141         var colCount = cm.getColumnCount();
48142
48143         if(this.grid.monitorWindowResize === true){
48144             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48145         }
48146         var header = this.renderHeaders();
48147         var body = this.templates.body.apply({rows:""});
48148         var html = this.templates.master.apply({
48149             lockedBody: body,
48150             body: body,
48151             lockedHeader: header[0],
48152             header: header[1]
48153         });
48154
48155         //this.updateColumns();
48156
48157         this.grid.getGridEl().dom.innerHTML = html;
48158
48159         this.initElements();
48160         
48161         // a kludge to fix the random scolling effect in webkit
48162         this.el.on("scroll", function() {
48163             this.el.dom.scrollTop=0; // hopefully not recursive..
48164         },this);
48165
48166         this.scroller.on("scroll", this.handleScroll, this);
48167         this.lockedBody.on("mousewheel", this.handleWheel, this);
48168         this.mainBody.on("mousewheel", this.handleWheel, this);
48169
48170         this.mainHd.on("mouseover", this.handleHdOver, this);
48171         this.mainHd.on("mouseout", this.handleHdOut, this);
48172         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48173                 {delegate: "."+this.splitClass});
48174
48175         this.lockedHd.on("mouseover", this.handleHdOver, this);
48176         this.lockedHd.on("mouseout", this.handleHdOut, this);
48177         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48178                 {delegate: "."+this.splitClass});
48179
48180         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48181             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48182         }
48183
48184         this.updateSplitters();
48185
48186         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48187             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48188             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48189         }
48190
48191         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48192             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48193             this.hmenu.add(
48194                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48195                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48196             );
48197             if(this.grid.enableColLock !== false){
48198                 this.hmenu.add('-',
48199                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48200                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48201                 );
48202             }
48203             if(this.grid.enableColumnHide !== false){
48204
48205                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48206                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48207                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48208
48209                 this.hmenu.add('-',
48210                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48211                 );
48212             }
48213             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48214
48215             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48216         }
48217
48218         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48219             this.dd = new Roo.grid.GridDragZone(this.grid, {
48220                 ddGroup : this.grid.ddGroup || 'GridDD'
48221             });
48222         }
48223
48224         /*
48225         for(var i = 0; i < colCount; i++){
48226             if(cm.isHidden(i)){
48227                 this.hideColumn(i);
48228             }
48229             if(cm.config[i].align){
48230                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48231                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48232             }
48233         }*/
48234         
48235         this.updateHeaderSortState();
48236
48237         this.beforeInitialResize();
48238         this.layout(true);
48239
48240         // two part rendering gives faster view to the user
48241         this.renderPhase2.defer(1, this);
48242     },
48243
48244     renderPhase2 : function(){
48245         // render the rows now
48246         this.refresh();
48247         if(this.grid.autoSizeColumns){
48248             this.autoSizeColumns();
48249         }
48250     },
48251
48252     beforeInitialResize : function(){
48253
48254     },
48255
48256     onColumnSplitterMoved : function(i, w){
48257         this.userResized = true;
48258         var cm = this.grid.colModel;
48259         cm.setColumnWidth(i, w, true);
48260         var cid = cm.getColumnId(i);
48261         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48262         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48263         this.updateSplitters();
48264         this.layout();
48265         this.grid.fireEvent("columnresize", i, w);
48266     },
48267
48268     syncRowHeights : function(startIndex, endIndex){
48269         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48270             startIndex = startIndex || 0;
48271             var mrows = this.getBodyTable().rows;
48272             var lrows = this.getLockedTable().rows;
48273             var len = mrows.length-1;
48274             endIndex = Math.min(endIndex || len, len);
48275             for(var i = startIndex; i <= endIndex; i++){
48276                 var m = mrows[i], l = lrows[i];
48277                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48278                 m.style.height = l.style.height = h + "px";
48279             }
48280         }
48281     },
48282
48283     layout : function(initialRender, is2ndPass){
48284         var g = this.grid;
48285         var auto = g.autoHeight;
48286         var scrollOffset = 16;
48287         var c = g.getGridEl(), cm = this.cm,
48288                 expandCol = g.autoExpandColumn,
48289                 gv = this;
48290         //c.beginMeasure();
48291
48292         if(!c.dom.offsetWidth){ // display:none?
48293             if(initialRender){
48294                 this.lockedWrap.show();
48295                 this.mainWrap.show();
48296             }
48297             return;
48298         }
48299
48300         var hasLock = this.cm.isLocked(0);
48301
48302         var tbh = this.headerPanel.getHeight();
48303         var bbh = this.footerPanel.getHeight();
48304
48305         if(auto){
48306             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48307             var newHeight = ch + c.getBorderWidth("tb");
48308             if(g.maxHeight){
48309                 newHeight = Math.min(g.maxHeight, newHeight);
48310             }
48311             c.setHeight(newHeight);
48312         }
48313
48314         if(g.autoWidth){
48315             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48316         }
48317
48318         var s = this.scroller;
48319
48320         var csize = c.getSize(true);
48321
48322         this.el.setSize(csize.width, csize.height);
48323
48324         this.headerPanel.setWidth(csize.width);
48325         this.footerPanel.setWidth(csize.width);
48326
48327         var hdHeight = this.mainHd.getHeight();
48328         var vw = csize.width;
48329         var vh = csize.height - (tbh + bbh);
48330
48331         s.setSize(vw, vh);
48332
48333         var bt = this.getBodyTable();
48334         var ltWidth = hasLock ?
48335                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48336
48337         var scrollHeight = bt.offsetHeight;
48338         var scrollWidth = ltWidth + bt.offsetWidth;
48339         var vscroll = false, hscroll = false;
48340
48341         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48342
48343         var lw = this.lockedWrap, mw = this.mainWrap;
48344         var lb = this.lockedBody, mb = this.mainBody;
48345
48346         setTimeout(function(){
48347             var t = s.dom.offsetTop;
48348             var w = s.dom.clientWidth,
48349                 h = s.dom.clientHeight;
48350
48351             lw.setTop(t);
48352             lw.setSize(ltWidth, h);
48353
48354             mw.setLeftTop(ltWidth, t);
48355             mw.setSize(w-ltWidth, h);
48356
48357             lb.setHeight(h-hdHeight);
48358             mb.setHeight(h-hdHeight);
48359
48360             if(is2ndPass !== true && !gv.userResized && expandCol){
48361                 // high speed resize without full column calculation
48362                 
48363                 var ci = cm.getIndexById(expandCol);
48364                 if (ci < 0) {
48365                     ci = cm.findColumnIndex(expandCol);
48366                 }
48367                 ci = Math.max(0, ci); // make sure it's got at least the first col.
48368                 var expandId = cm.getColumnId(ci);
48369                 var  tw = cm.getTotalWidth(false);
48370                 var currentWidth = cm.getColumnWidth(ci);
48371                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
48372                 if(currentWidth != cw){
48373                     cm.setColumnWidth(ci, cw, true);
48374                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48375                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48376                     gv.updateSplitters();
48377                     gv.layout(false, true);
48378                 }
48379             }
48380
48381             if(initialRender){
48382                 lw.show();
48383                 mw.show();
48384             }
48385             //c.endMeasure();
48386         }, 10);
48387     },
48388
48389     onWindowResize : function(){
48390         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
48391             return;
48392         }
48393         this.layout();
48394     },
48395
48396     appendFooter : function(parentEl){
48397         return null;
48398     },
48399
48400     sortAscText : "Sort Ascending",
48401     sortDescText : "Sort Descending",
48402     lockText : "Lock Column",
48403     unlockText : "Unlock Column",
48404     columnsText : "Columns"
48405 });
48406
48407
48408 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
48409     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
48410     this.proxy.el.addClass('x-grid3-col-dd');
48411 };
48412
48413 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
48414     handleMouseDown : function(e){
48415
48416     },
48417
48418     callHandleMouseDown : function(e){
48419         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
48420     }
48421 });
48422 /*
48423  * Based on:
48424  * Ext JS Library 1.1.1
48425  * Copyright(c) 2006-2007, Ext JS, LLC.
48426  *
48427  * Originally Released Under LGPL - original licence link has changed is not relivant.
48428  *
48429  * Fork - LGPL
48430  * <script type="text/javascript">
48431  */
48432  
48433 // private
48434 // This is a support class used internally by the Grid components
48435 Roo.grid.SplitDragZone = function(grid, hd, hd2){
48436     this.grid = grid;
48437     this.view = grid.getView();
48438     this.proxy = this.view.resizeProxy;
48439     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
48440         "gridSplitters" + this.grid.getGridEl().id, {
48441         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
48442     });
48443     this.setHandleElId(Roo.id(hd));
48444     this.setOuterHandleElId(Roo.id(hd2));
48445     this.scroll = false;
48446 };
48447 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
48448     fly: Roo.Element.fly,
48449
48450     b4StartDrag : function(x, y){
48451         this.view.headersDisabled = true;
48452         this.proxy.setHeight(this.view.mainWrap.getHeight());
48453         var w = this.cm.getColumnWidth(this.cellIndex);
48454         var minw = Math.max(w-this.grid.minColumnWidth, 0);
48455         this.resetConstraints();
48456         this.setXConstraint(minw, 1000);
48457         this.setYConstraint(0, 0);
48458         this.minX = x - minw;
48459         this.maxX = x + 1000;
48460         this.startPos = x;
48461         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
48462     },
48463
48464
48465     handleMouseDown : function(e){
48466         ev = Roo.EventObject.setEvent(e);
48467         var t = this.fly(ev.getTarget());
48468         if(t.hasClass("x-grid-split")){
48469             this.cellIndex = this.view.getCellIndex(t.dom);
48470             this.split = t.dom;
48471             this.cm = this.grid.colModel;
48472             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
48473                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
48474             }
48475         }
48476     },
48477
48478     endDrag : function(e){
48479         this.view.headersDisabled = false;
48480         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
48481         var diff = endX - this.startPos;
48482         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
48483     },
48484
48485     autoOffset : function(){
48486         this.setDelta(0,0);
48487     }
48488 });/*
48489  * Based on:
48490  * Ext JS Library 1.1.1
48491  * Copyright(c) 2006-2007, Ext JS, LLC.
48492  *
48493  * Originally Released Under LGPL - original licence link has changed is not relivant.
48494  *
48495  * Fork - LGPL
48496  * <script type="text/javascript">
48497  */
48498  
48499 // private
48500 // This is a support class used internally by the Grid components
48501 Roo.grid.GridDragZone = function(grid, config){
48502     this.view = grid.getView();
48503     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
48504     if(this.view.lockedBody){
48505         this.setHandleElId(Roo.id(this.view.mainBody.dom));
48506         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
48507     }
48508     this.scroll = false;
48509     this.grid = grid;
48510     this.ddel = document.createElement('div');
48511     this.ddel.className = 'x-grid-dd-wrap';
48512 };
48513
48514 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
48515     ddGroup : "GridDD",
48516
48517     getDragData : function(e){
48518         var t = Roo.lib.Event.getTarget(e);
48519         var rowIndex = this.view.findRowIndex(t);
48520         if(rowIndex !== false){
48521             var sm = this.grid.selModel;
48522             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
48523               //  sm.mouseDown(e, t);
48524             //}
48525             if (e.hasModifier()){
48526                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
48527             }
48528             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
48529         }
48530         return false;
48531     },
48532
48533     onInitDrag : function(e){
48534         var data = this.dragData;
48535         this.ddel.innerHTML = this.grid.getDragDropText();
48536         this.proxy.update(this.ddel);
48537         // fire start drag?
48538     },
48539
48540     afterRepair : function(){
48541         this.dragging = false;
48542     },
48543
48544     getRepairXY : function(e, data){
48545         return false;
48546     },
48547
48548     onEndDrag : function(data, e){
48549         // fire end drag?
48550     },
48551
48552     onValidDrop : function(dd, e, id){
48553         // fire drag drop?
48554         this.hideProxy();
48555     },
48556
48557     beforeInvalidDrop : function(e, id){
48558
48559     }
48560 });/*
48561  * Based on:
48562  * Ext JS Library 1.1.1
48563  * Copyright(c) 2006-2007, Ext JS, LLC.
48564  *
48565  * Originally Released Under LGPL - original licence link has changed is not relivant.
48566  *
48567  * Fork - LGPL
48568  * <script type="text/javascript">
48569  */
48570  
48571
48572 /**
48573  * @class Roo.grid.ColumnModel
48574  * @extends Roo.util.Observable
48575  * This is the default implementation of a ColumnModel used by the Grid. It defines
48576  * the columns in the grid.
48577  * <br>Usage:<br>
48578  <pre><code>
48579  var colModel = new Roo.grid.ColumnModel([
48580         {header: "Ticker", width: 60, sortable: true, locked: true},
48581         {header: "Company Name", width: 150, sortable: true},
48582         {header: "Market Cap.", width: 100, sortable: true},
48583         {header: "$ Sales", width: 100, sortable: true, renderer: money},
48584         {header: "Employees", width: 100, sortable: true, resizable: false}
48585  ]);
48586  </code></pre>
48587  * <p>
48588  
48589  * The config options listed for this class are options which may appear in each
48590  * individual column definition.
48591  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
48592  * @constructor
48593  * @param {Object} config An Array of column config objects. See this class's
48594  * config objects for details.
48595 */
48596 Roo.grid.ColumnModel = function(config){
48597         /**
48598      * The config passed into the constructor
48599      */
48600     this.config = config;
48601     this.lookup = {};
48602
48603     // if no id, create one
48604     // if the column does not have a dataIndex mapping,
48605     // map it to the order it is in the config
48606     for(var i = 0, len = config.length; i < len; i++){
48607         var c = config[i];
48608         if(typeof c.dataIndex == "undefined"){
48609             c.dataIndex = i;
48610         }
48611         if(typeof c.renderer == "string"){
48612             c.renderer = Roo.util.Format[c.renderer];
48613         }
48614         if(typeof c.id == "undefined"){
48615             c.id = Roo.id();
48616         }
48617         if(c.editor && c.editor.xtype){
48618             c.editor  = Roo.factory(c.editor, Roo.grid);
48619         }
48620         if(c.editor && c.editor.isFormField){
48621             c.editor = new Roo.grid.GridEditor(c.editor);
48622         }
48623         this.lookup[c.id] = c;
48624     }
48625
48626     /**
48627      * The width of columns which have no width specified (defaults to 100)
48628      * @type Number
48629      */
48630     this.defaultWidth = 100;
48631
48632     /**
48633      * Default sortable of columns which have no sortable specified (defaults to false)
48634      * @type Boolean
48635      */
48636     this.defaultSortable = false;
48637
48638     this.addEvents({
48639         /**
48640              * @event widthchange
48641              * Fires when the width of a column changes.
48642              * @param {ColumnModel} this
48643              * @param {Number} columnIndex The column index
48644              * @param {Number} newWidth The new width
48645              */
48646             "widthchange": true,
48647         /**
48648              * @event headerchange
48649              * Fires when the text of a header changes.
48650              * @param {ColumnModel} this
48651              * @param {Number} columnIndex The column index
48652              * @param {Number} newText The new header text
48653              */
48654             "headerchange": true,
48655         /**
48656              * @event hiddenchange
48657              * Fires when a column is hidden or "unhidden".
48658              * @param {ColumnModel} this
48659              * @param {Number} columnIndex The column index
48660              * @param {Boolean} hidden true if hidden, false otherwise
48661              */
48662             "hiddenchange": true,
48663             /**
48664          * @event columnmoved
48665          * Fires when a column is moved.
48666          * @param {ColumnModel} this
48667          * @param {Number} oldIndex
48668          * @param {Number} newIndex
48669          */
48670         "columnmoved" : true,
48671         /**
48672          * @event columlockchange
48673          * Fires when a column's locked state is changed
48674          * @param {ColumnModel} this
48675          * @param {Number} colIndex
48676          * @param {Boolean} locked true if locked
48677          */
48678         "columnlockchange" : true
48679     });
48680     Roo.grid.ColumnModel.superclass.constructor.call(this);
48681 };
48682 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
48683     /**
48684      * @cfg {String} header The header text to display in the Grid view.
48685      */
48686     /**
48687      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
48688      * {@link Roo.data.Record} definition from which to draw the column's value. If not
48689      * specified, the column's index is used as an index into the Record's data Array.
48690      */
48691     /**
48692      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
48693      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
48694      */
48695     /**
48696      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
48697      * Defaults to the value of the {@link #defaultSortable} property.
48698      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
48699      */
48700     /**
48701      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
48702      */
48703     /**
48704      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
48705      */
48706     /**
48707      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
48708      */
48709     /**
48710      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
48711      */
48712     /**
48713      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
48714      * given the cell's data value. See {@link #setRenderer}. If not specified, the
48715      * default renderer uses the raw data value.
48716      */
48717        /**
48718      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48719      */
48720     /**
48721      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48722      */
48723
48724     /**
48725      * Returns the id of the column at the specified index.
48726      * @param {Number} index The column index
48727      * @return {String} the id
48728      */
48729     getColumnId : function(index){
48730         return this.config[index].id;
48731     },
48732
48733     /**
48734      * Returns the column for a specified id.
48735      * @param {String} id The column id
48736      * @return {Object} the column
48737      */
48738     getColumnById : function(id){
48739         return this.lookup[id];
48740     },
48741
48742     
48743     /**
48744      * Returns the column for a specified dataIndex.
48745      * @param {String} dataIndex The column dataIndex
48746      * @return {Object|Boolean} the column or false if not found
48747      */
48748     getColumnByDataIndex: function(dataIndex){
48749         var index = this.findColumnIndex(dataIndex);
48750         return index > -1 ? this.config[index] : false;
48751     },
48752     
48753     /**
48754      * Returns the index for a specified column id.
48755      * @param {String} id The column id
48756      * @return {Number} the index, or -1 if not found
48757      */
48758     getIndexById : function(id){
48759         for(var i = 0, len = this.config.length; i < len; i++){
48760             if(this.config[i].id == id){
48761                 return i;
48762             }
48763         }
48764         return -1;
48765     },
48766     
48767     /**
48768      * Returns the index for a specified column dataIndex.
48769      * @param {String} dataIndex The column dataIndex
48770      * @return {Number} the index, or -1 if not found
48771      */
48772     
48773     findColumnIndex : function(dataIndex){
48774         for(var i = 0, len = this.config.length; i < len; i++){
48775             if(this.config[i].dataIndex == dataIndex){
48776                 return i;
48777             }
48778         }
48779         return -1;
48780     },
48781     
48782     
48783     moveColumn : function(oldIndex, newIndex){
48784         var c = this.config[oldIndex];
48785         this.config.splice(oldIndex, 1);
48786         this.config.splice(newIndex, 0, c);
48787         this.dataMap = null;
48788         this.fireEvent("columnmoved", this, oldIndex, newIndex);
48789     },
48790
48791     isLocked : function(colIndex){
48792         return this.config[colIndex].locked === true;
48793     },
48794
48795     setLocked : function(colIndex, value, suppressEvent){
48796         if(this.isLocked(colIndex) == value){
48797             return;
48798         }
48799         this.config[colIndex].locked = value;
48800         if(!suppressEvent){
48801             this.fireEvent("columnlockchange", this, colIndex, value);
48802         }
48803     },
48804
48805     getTotalLockedWidth : function(){
48806         var totalWidth = 0;
48807         for(var i = 0; i < this.config.length; i++){
48808             if(this.isLocked(i) && !this.isHidden(i)){
48809                 this.totalWidth += this.getColumnWidth(i);
48810             }
48811         }
48812         return totalWidth;
48813     },
48814
48815     getLockedCount : function(){
48816         for(var i = 0, len = this.config.length; i < len; i++){
48817             if(!this.isLocked(i)){
48818                 return i;
48819             }
48820         }
48821     },
48822
48823     /**
48824      * Returns the number of columns.
48825      * @return {Number}
48826      */
48827     getColumnCount : function(visibleOnly){
48828         if(visibleOnly === true){
48829             var c = 0;
48830             for(var i = 0, len = this.config.length; i < len; i++){
48831                 if(!this.isHidden(i)){
48832                     c++;
48833                 }
48834             }
48835             return c;
48836         }
48837         return this.config.length;
48838     },
48839
48840     /**
48841      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
48842      * @param {Function} fn
48843      * @param {Object} scope (optional)
48844      * @return {Array} result
48845      */
48846     getColumnsBy : function(fn, scope){
48847         var r = [];
48848         for(var i = 0, len = this.config.length; i < len; i++){
48849             var c = this.config[i];
48850             if(fn.call(scope||this, c, i) === true){
48851                 r[r.length] = c;
48852             }
48853         }
48854         return r;
48855     },
48856
48857     /**
48858      * Returns true if the specified column is sortable.
48859      * @param {Number} col The column index
48860      * @return {Boolean}
48861      */
48862     isSortable : function(col){
48863         if(typeof this.config[col].sortable == "undefined"){
48864             return this.defaultSortable;
48865         }
48866         return this.config[col].sortable;
48867     },
48868
48869     /**
48870      * Returns the rendering (formatting) function defined for the column.
48871      * @param {Number} col The column index.
48872      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
48873      */
48874     getRenderer : function(col){
48875         if(!this.config[col].renderer){
48876             return Roo.grid.ColumnModel.defaultRenderer;
48877         }
48878         return this.config[col].renderer;
48879     },
48880
48881     /**
48882      * Sets the rendering (formatting) function for a column.
48883      * @param {Number} col The column index
48884      * @param {Function} fn The function to use to process the cell's raw data
48885      * to return HTML markup for the grid view. The render function is called with
48886      * the following parameters:<ul>
48887      * <li>Data value.</li>
48888      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
48889      * <li>css A CSS style string to apply to the table cell.</li>
48890      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
48891      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
48892      * <li>Row index</li>
48893      * <li>Column index</li>
48894      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
48895      */
48896     setRenderer : function(col, fn){
48897         this.config[col].renderer = fn;
48898     },
48899
48900     /**
48901      * Returns the width for the specified column.
48902      * @param {Number} col The column index
48903      * @return {Number}
48904      */
48905     getColumnWidth : function(col){
48906         return this.config[col].width || this.defaultWidth;
48907     },
48908
48909     /**
48910      * Sets the width for a column.
48911      * @param {Number} col The column index
48912      * @param {Number} width The new width
48913      */
48914     setColumnWidth : function(col, width, suppressEvent){
48915         this.config[col].width = width;
48916         this.totalWidth = null;
48917         if(!suppressEvent){
48918              this.fireEvent("widthchange", this, col, width);
48919         }
48920     },
48921
48922     /**
48923      * Returns the total width of all columns.
48924      * @param {Boolean} includeHidden True to include hidden column widths
48925      * @return {Number}
48926      */
48927     getTotalWidth : function(includeHidden){
48928         if(!this.totalWidth){
48929             this.totalWidth = 0;
48930             for(var i = 0, len = this.config.length; i < len; i++){
48931                 if(includeHidden || !this.isHidden(i)){
48932                     this.totalWidth += this.getColumnWidth(i);
48933                 }
48934             }
48935         }
48936         return this.totalWidth;
48937     },
48938
48939     /**
48940      * Returns the header for the specified column.
48941      * @param {Number} col The column index
48942      * @return {String}
48943      */
48944     getColumnHeader : function(col){
48945         return this.config[col].header;
48946     },
48947
48948     /**
48949      * Sets the header for a column.
48950      * @param {Number} col The column index
48951      * @param {String} header The new header
48952      */
48953     setColumnHeader : function(col, header){
48954         this.config[col].header = header;
48955         this.fireEvent("headerchange", this, col, header);
48956     },
48957
48958     /**
48959      * Returns the tooltip for the specified column.
48960      * @param {Number} col The column index
48961      * @return {String}
48962      */
48963     getColumnTooltip : function(col){
48964             return this.config[col].tooltip;
48965     },
48966     /**
48967      * Sets the tooltip for a column.
48968      * @param {Number} col The column index
48969      * @param {String} tooltip The new tooltip
48970      */
48971     setColumnTooltip : function(col, tooltip){
48972             this.config[col].tooltip = tooltip;
48973     },
48974
48975     /**
48976      * Returns the dataIndex for the specified column.
48977      * @param {Number} col The column index
48978      * @return {Number}
48979      */
48980     getDataIndex : function(col){
48981         return this.config[col].dataIndex;
48982     },
48983
48984     /**
48985      * Sets the dataIndex for a column.
48986      * @param {Number} col The column index
48987      * @param {Number} dataIndex The new dataIndex
48988      */
48989     setDataIndex : function(col, dataIndex){
48990         this.config[col].dataIndex = dataIndex;
48991     },
48992
48993     
48994     
48995     /**
48996      * Returns true if the cell is editable.
48997      * @param {Number} colIndex The column index
48998      * @param {Number} rowIndex The row index
48999      * @return {Boolean}
49000      */
49001     isCellEditable : function(colIndex, rowIndex){
49002         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49003     },
49004
49005     /**
49006      * Returns the editor defined for the cell/column.
49007      * return false or null to disable editing.
49008      * @param {Number} colIndex The column index
49009      * @param {Number} rowIndex The row index
49010      * @return {Object}
49011      */
49012     getCellEditor : function(colIndex, rowIndex){
49013         return this.config[colIndex].editor;
49014     },
49015
49016     /**
49017      * Sets if a column is editable.
49018      * @param {Number} col The column index
49019      * @param {Boolean} editable True if the column is editable
49020      */
49021     setEditable : function(col, editable){
49022         this.config[col].editable = editable;
49023     },
49024
49025
49026     /**
49027      * Returns true if the column is hidden.
49028      * @param {Number} colIndex The column index
49029      * @return {Boolean}
49030      */
49031     isHidden : function(colIndex){
49032         return this.config[colIndex].hidden;
49033     },
49034
49035
49036     /**
49037      * Returns true if the column width cannot be changed
49038      */
49039     isFixed : function(colIndex){
49040         return this.config[colIndex].fixed;
49041     },
49042
49043     /**
49044      * Returns true if the column can be resized
49045      * @return {Boolean}
49046      */
49047     isResizable : function(colIndex){
49048         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49049     },
49050     /**
49051      * Sets if a column is hidden.
49052      * @param {Number} colIndex The column index
49053      * @param {Boolean} hidden True if the column is hidden
49054      */
49055     setHidden : function(colIndex, hidden){
49056         this.config[colIndex].hidden = hidden;
49057         this.totalWidth = null;
49058         this.fireEvent("hiddenchange", this, colIndex, hidden);
49059     },
49060
49061     /**
49062      * Sets the editor for a column.
49063      * @param {Number} col The column index
49064      * @param {Object} editor The editor object
49065      */
49066     setEditor : function(col, editor){
49067         this.config[col].editor = editor;
49068     }
49069 });
49070
49071 Roo.grid.ColumnModel.defaultRenderer = function(value){
49072         if(typeof value == "string" && value.length < 1){
49073             return "&#160;";
49074         }
49075         return value;
49076 };
49077
49078 // Alias for backwards compatibility
49079 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49080 /*
49081  * Based on:
49082  * Ext JS Library 1.1.1
49083  * Copyright(c) 2006-2007, Ext JS, LLC.
49084  *
49085  * Originally Released Under LGPL - original licence link has changed is not relivant.
49086  *
49087  * Fork - LGPL
49088  * <script type="text/javascript">
49089  */
49090
49091 /**
49092  * @class Roo.grid.AbstractSelectionModel
49093  * @extends Roo.util.Observable
49094  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49095  * implemented by descendant classes.  This class should not be directly instantiated.
49096  * @constructor
49097  */
49098 Roo.grid.AbstractSelectionModel = function(){
49099     this.locked = false;
49100     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49101 };
49102
49103 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49104     /** @ignore Called by the grid automatically. Do not call directly. */
49105     init : function(grid){
49106         this.grid = grid;
49107         this.initEvents();
49108     },
49109
49110     /**
49111      * Locks the selections.
49112      */
49113     lock : function(){
49114         this.locked = true;
49115     },
49116
49117     /**
49118      * Unlocks the selections.
49119      */
49120     unlock : function(){
49121         this.locked = false;
49122     },
49123
49124     /**
49125      * Returns true if the selections are locked.
49126      * @return {Boolean}
49127      */
49128     isLocked : function(){
49129         return this.locked;
49130     }
49131 });/*
49132  * Based on:
49133  * Ext JS Library 1.1.1
49134  * Copyright(c) 2006-2007, Ext JS, LLC.
49135  *
49136  * Originally Released Under LGPL - original licence link has changed is not relivant.
49137  *
49138  * Fork - LGPL
49139  * <script type="text/javascript">
49140  */
49141 /**
49142  * @extends Roo.grid.AbstractSelectionModel
49143  * @class Roo.grid.RowSelectionModel
49144  * The default SelectionModel used by {@link Roo.grid.Grid}.
49145  * It supports multiple selections and keyboard selection/navigation. 
49146  * @constructor
49147  * @param {Object} config
49148  */
49149 Roo.grid.RowSelectionModel = function(config){
49150     Roo.apply(this, config);
49151     this.selections = new Roo.util.MixedCollection(false, function(o){
49152         return o.id;
49153     });
49154
49155     this.last = false;
49156     this.lastActive = false;
49157
49158     this.addEvents({
49159         /**
49160              * @event selectionchange
49161              * Fires when the selection changes
49162              * @param {SelectionModel} this
49163              */
49164             "selectionchange" : true,
49165         /**
49166              * @event afterselectionchange
49167              * Fires after the selection changes (eg. by key press or clicking)
49168              * @param {SelectionModel} this
49169              */
49170             "afterselectionchange" : true,
49171         /**
49172              * @event beforerowselect
49173              * Fires when a row is selected being selected, return false to cancel.
49174              * @param {SelectionModel} this
49175              * @param {Number} rowIndex The selected index
49176              * @param {Boolean} keepExisting False if other selections will be cleared
49177              */
49178             "beforerowselect" : true,
49179         /**
49180              * @event rowselect
49181              * Fires when a row is selected.
49182              * @param {SelectionModel} this
49183              * @param {Number} rowIndex The selected index
49184              * @param {Roo.data.Record} r The record
49185              */
49186             "rowselect" : true,
49187         /**
49188              * @event rowdeselect
49189              * Fires when a row is deselected.
49190              * @param {SelectionModel} this
49191              * @param {Number} rowIndex The selected index
49192              */
49193         "rowdeselect" : true
49194     });
49195     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49196     this.locked = false;
49197 };
49198
49199 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49200     /**
49201      * @cfg {Boolean} singleSelect
49202      * True to allow selection of only one row at a time (defaults to false)
49203      */
49204     singleSelect : false,
49205
49206     // private
49207     initEvents : function(){
49208
49209         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49210             this.grid.on("mousedown", this.handleMouseDown, this);
49211         }else{ // allow click to work like normal
49212             this.grid.on("rowclick", this.handleDragableRowClick, this);
49213         }
49214
49215         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49216             "up" : function(e){
49217                 if(!e.shiftKey){
49218                     this.selectPrevious(e.shiftKey);
49219                 }else if(this.last !== false && this.lastActive !== false){
49220                     var last = this.last;
49221                     this.selectRange(this.last,  this.lastActive-1);
49222                     this.grid.getView().focusRow(this.lastActive);
49223                     if(last !== false){
49224                         this.last = last;
49225                     }
49226                 }else{
49227                     this.selectFirstRow();
49228                 }
49229                 this.fireEvent("afterselectionchange", this);
49230             },
49231             "down" : function(e){
49232                 if(!e.shiftKey){
49233                     this.selectNext(e.shiftKey);
49234                 }else if(this.last !== false && this.lastActive !== false){
49235                     var last = this.last;
49236                     this.selectRange(this.last,  this.lastActive+1);
49237                     this.grid.getView().focusRow(this.lastActive);
49238                     if(last !== false){
49239                         this.last = last;
49240                     }
49241                 }else{
49242                     this.selectFirstRow();
49243                 }
49244                 this.fireEvent("afterselectionchange", this);
49245             },
49246             scope: this
49247         });
49248
49249         var view = this.grid.view;
49250         view.on("refresh", this.onRefresh, this);
49251         view.on("rowupdated", this.onRowUpdated, this);
49252         view.on("rowremoved", this.onRemove, this);
49253     },
49254
49255     // private
49256     onRefresh : function(){
49257         var ds = this.grid.dataSource, i, v = this.grid.view;
49258         var s = this.selections;
49259         s.each(function(r){
49260             if((i = ds.indexOfId(r.id)) != -1){
49261                 v.onRowSelect(i);
49262             }else{
49263                 s.remove(r);
49264             }
49265         });
49266     },
49267
49268     // private
49269     onRemove : function(v, index, r){
49270         this.selections.remove(r);
49271     },
49272
49273     // private
49274     onRowUpdated : function(v, index, r){
49275         if(this.isSelected(r)){
49276             v.onRowSelect(index);
49277         }
49278     },
49279
49280     /**
49281      * Select records.
49282      * @param {Array} records The records to select
49283      * @param {Boolean} keepExisting (optional) True to keep existing selections
49284      */
49285     selectRecords : function(records, keepExisting){
49286         if(!keepExisting){
49287             this.clearSelections();
49288         }
49289         var ds = this.grid.dataSource;
49290         for(var i = 0, len = records.length; i < len; i++){
49291             this.selectRow(ds.indexOf(records[i]), true);
49292         }
49293     },
49294
49295     /**
49296      * Gets the number of selected rows.
49297      * @return {Number}
49298      */
49299     getCount : function(){
49300         return this.selections.length;
49301     },
49302
49303     /**
49304      * Selects the first row in the grid.
49305      */
49306     selectFirstRow : function(){
49307         this.selectRow(0);
49308     },
49309
49310     /**
49311      * Select the last row.
49312      * @param {Boolean} keepExisting (optional) True to keep existing selections
49313      */
49314     selectLastRow : function(keepExisting){
49315         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49316     },
49317
49318     /**
49319      * Selects the row immediately following the last selected row.
49320      * @param {Boolean} keepExisting (optional) True to keep existing selections
49321      */
49322     selectNext : function(keepExisting){
49323         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49324             this.selectRow(this.last+1, keepExisting);
49325             this.grid.getView().focusRow(this.last);
49326         }
49327     },
49328
49329     /**
49330      * Selects the row that precedes the last selected row.
49331      * @param {Boolean} keepExisting (optional) True to keep existing selections
49332      */
49333     selectPrevious : function(keepExisting){
49334         if(this.last){
49335             this.selectRow(this.last-1, keepExisting);
49336             this.grid.getView().focusRow(this.last);
49337         }
49338     },
49339
49340     /**
49341      * Returns the selected records
49342      * @return {Array} Array of selected records
49343      */
49344     getSelections : function(){
49345         return [].concat(this.selections.items);
49346     },
49347
49348     /**
49349      * Returns the first selected record.
49350      * @return {Record}
49351      */
49352     getSelected : function(){
49353         return this.selections.itemAt(0);
49354     },
49355
49356
49357     /**
49358      * Clears all selections.
49359      */
49360     clearSelections : function(fast){
49361         if(this.locked) return;
49362         if(fast !== true){
49363             var ds = this.grid.dataSource;
49364             var s = this.selections;
49365             s.each(function(r){
49366                 this.deselectRow(ds.indexOfId(r.id));
49367             }, this);
49368             s.clear();
49369         }else{
49370             this.selections.clear();
49371         }
49372         this.last = false;
49373     },
49374
49375
49376     /**
49377      * Selects all rows.
49378      */
49379     selectAll : function(){
49380         if(this.locked) return;
49381         this.selections.clear();
49382         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
49383             this.selectRow(i, true);
49384         }
49385     },
49386
49387     /**
49388      * Returns True if there is a selection.
49389      * @return {Boolean}
49390      */
49391     hasSelection : function(){
49392         return this.selections.length > 0;
49393     },
49394
49395     /**
49396      * Returns True if the specified row is selected.
49397      * @param {Number/Record} record The record or index of the record to check
49398      * @return {Boolean}
49399      */
49400     isSelected : function(index){
49401         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
49402         return (r && this.selections.key(r.id) ? true : false);
49403     },
49404
49405     /**
49406      * Returns True if the specified record id is selected.
49407      * @param {String} id The id of record to check
49408      * @return {Boolean}
49409      */
49410     isIdSelected : function(id){
49411         return (this.selections.key(id) ? true : false);
49412     },
49413
49414     // private
49415     handleMouseDown : function(e, t){
49416         var view = this.grid.getView(), rowIndex;
49417         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
49418             return;
49419         };
49420         if(e.shiftKey && this.last !== false){
49421             var last = this.last;
49422             this.selectRange(last, rowIndex, e.ctrlKey);
49423             this.last = last; // reset the last
49424             view.focusRow(rowIndex);
49425         }else{
49426             var isSelected = this.isSelected(rowIndex);
49427             if(e.button !== 0 && isSelected){
49428                 view.focusRow(rowIndex);
49429             }else if(e.ctrlKey && isSelected){
49430                 this.deselectRow(rowIndex);
49431             }else if(!isSelected){
49432                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
49433                 view.focusRow(rowIndex);
49434             }
49435         }
49436         this.fireEvent("afterselectionchange", this);
49437     },
49438     // private
49439     handleDragableRowClick :  function(grid, rowIndex, e) 
49440     {
49441         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
49442             this.selectRow(rowIndex, false);
49443             grid.view.focusRow(rowIndex);
49444              this.fireEvent("afterselectionchange", this);
49445         }
49446     },
49447     
49448     /**
49449      * Selects multiple rows.
49450      * @param {Array} rows Array of the indexes of the row to select
49451      * @param {Boolean} keepExisting (optional) True to keep existing selections
49452      */
49453     selectRows : function(rows, keepExisting){
49454         if(!keepExisting){
49455             this.clearSelections();
49456         }
49457         for(var i = 0, len = rows.length; i < len; i++){
49458             this.selectRow(rows[i], true);
49459         }
49460     },
49461
49462     /**
49463      * Selects a range of rows. All rows in between startRow and endRow are also selected.
49464      * @param {Number} startRow The index of the first row in the range
49465      * @param {Number} endRow The index of the last row in the range
49466      * @param {Boolean} keepExisting (optional) True to retain existing selections
49467      */
49468     selectRange : function(startRow, endRow, keepExisting){
49469         if(this.locked) return;
49470         if(!keepExisting){
49471             this.clearSelections();
49472         }
49473         if(startRow <= endRow){
49474             for(var i = startRow; i <= endRow; i++){
49475                 this.selectRow(i, true);
49476             }
49477         }else{
49478             for(var i = startRow; i >= endRow; i--){
49479                 this.selectRow(i, true);
49480             }
49481         }
49482     },
49483
49484     /**
49485      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
49486      * @param {Number} startRow The index of the first row in the range
49487      * @param {Number} endRow The index of the last row in the range
49488      */
49489     deselectRange : function(startRow, endRow, preventViewNotify){
49490         if(this.locked) return;
49491         for(var i = startRow; i <= endRow; i++){
49492             this.deselectRow(i, preventViewNotify);
49493         }
49494     },
49495
49496     /**
49497      * Selects a row.
49498      * @param {Number} row The index of the row to select
49499      * @param {Boolean} keepExisting (optional) True to keep existing selections
49500      */
49501     selectRow : function(index, keepExisting, preventViewNotify){
49502         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
49503         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
49504             if(!keepExisting || this.singleSelect){
49505                 this.clearSelections();
49506             }
49507             var r = this.grid.dataSource.getAt(index);
49508             this.selections.add(r);
49509             this.last = this.lastActive = index;
49510             if(!preventViewNotify){
49511                 this.grid.getView().onRowSelect(index);
49512             }
49513             this.fireEvent("rowselect", this, index, r);
49514             this.fireEvent("selectionchange", this);
49515         }
49516     },
49517
49518     /**
49519      * Deselects a row.
49520      * @param {Number} row The index of the row to deselect
49521      */
49522     deselectRow : function(index, preventViewNotify){
49523         if(this.locked) return;
49524         if(this.last == index){
49525             this.last = false;
49526         }
49527         if(this.lastActive == index){
49528             this.lastActive = false;
49529         }
49530         var r = this.grid.dataSource.getAt(index);
49531         this.selections.remove(r);
49532         if(!preventViewNotify){
49533             this.grid.getView().onRowDeselect(index);
49534         }
49535         this.fireEvent("rowdeselect", this, index);
49536         this.fireEvent("selectionchange", this);
49537     },
49538
49539     // private
49540     restoreLast : function(){
49541         if(this._last){
49542             this.last = this._last;
49543         }
49544     },
49545
49546     // private
49547     acceptsNav : function(row, col, cm){
49548         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49549     },
49550
49551     // private
49552     onEditorKey : function(field, e){
49553         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49554         if(k == e.TAB){
49555             e.stopEvent();
49556             ed.completeEdit();
49557             if(e.shiftKey){
49558                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49559             }else{
49560                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49561             }
49562         }else if(k == e.ENTER && !e.ctrlKey){
49563             e.stopEvent();
49564             ed.completeEdit();
49565             if(e.shiftKey){
49566                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
49567             }else{
49568                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
49569             }
49570         }else if(k == e.ESC){
49571             ed.cancelEdit();
49572         }
49573         if(newCell){
49574             g.startEditing(newCell[0], newCell[1]);
49575         }
49576     }
49577 });/*
49578  * Based on:
49579  * Ext JS Library 1.1.1
49580  * Copyright(c) 2006-2007, Ext JS, LLC.
49581  *
49582  * Originally Released Under LGPL - original licence link has changed is not relivant.
49583  *
49584  * Fork - LGPL
49585  * <script type="text/javascript">
49586  */
49587 /**
49588  * @class Roo.grid.CellSelectionModel
49589  * @extends Roo.grid.AbstractSelectionModel
49590  * This class provides the basic implementation for cell selection in a grid.
49591  * @constructor
49592  * @param {Object} config The object containing the configuration of this model.
49593  */
49594 Roo.grid.CellSelectionModel = function(config){
49595     Roo.apply(this, config);
49596
49597     this.selection = null;
49598
49599     this.addEvents({
49600         /**
49601              * @event beforerowselect
49602              * Fires before a cell is selected.
49603              * @param {SelectionModel} this
49604              * @param {Number} rowIndex The selected row index
49605              * @param {Number} colIndex The selected cell index
49606              */
49607             "beforecellselect" : true,
49608         /**
49609              * @event cellselect
49610              * Fires when a cell is selected.
49611              * @param {SelectionModel} this
49612              * @param {Number} rowIndex The selected row index
49613              * @param {Number} colIndex The selected cell index
49614              */
49615             "cellselect" : true,
49616         /**
49617              * @event selectionchange
49618              * Fires when the active selection changes.
49619              * @param {SelectionModel} this
49620              * @param {Object} selection null for no selection or an object (o) with two properties
49621                 <ul>
49622                 <li>o.record: the record object for the row the selection is in</li>
49623                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49624                 </ul>
49625              */
49626             "selectionchange" : true
49627     });
49628     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
49629 };
49630
49631 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
49632
49633     /** @ignore */
49634     initEvents : function(){
49635         this.grid.on("mousedown", this.handleMouseDown, this);
49636         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
49637         var view = this.grid.view;
49638         view.on("refresh", this.onViewChange, this);
49639         view.on("rowupdated", this.onRowUpdated, this);
49640         view.on("beforerowremoved", this.clearSelections, this);
49641         view.on("beforerowsinserted", this.clearSelections, this);
49642         if(this.grid.isEditor){
49643             this.grid.on("beforeedit", this.beforeEdit,  this);
49644         }
49645     },
49646
49647         //private
49648     beforeEdit : function(e){
49649         this.select(e.row, e.column, false, true, e.record);
49650     },
49651
49652         //private
49653     onRowUpdated : function(v, index, r){
49654         if(this.selection && this.selection.record == r){
49655             v.onCellSelect(index, this.selection.cell[1]);
49656         }
49657     },
49658
49659         //private
49660     onViewChange : function(){
49661         this.clearSelections(true);
49662     },
49663
49664         /**
49665          * Returns the currently selected cell,.
49666          * @return {Array} The selected cell (row, column) or null if none selected.
49667          */
49668     getSelectedCell : function(){
49669         return this.selection ? this.selection.cell : null;
49670     },
49671
49672     /**
49673      * Clears all selections.
49674      * @param {Boolean} true to prevent the gridview from being notified about the change.
49675      */
49676     clearSelections : function(preventNotify){
49677         var s = this.selection;
49678         if(s){
49679             if(preventNotify !== true){
49680                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
49681             }
49682             this.selection = null;
49683             this.fireEvent("selectionchange", this, null);
49684         }
49685     },
49686
49687     /**
49688      * Returns true if there is a selection.
49689      * @return {Boolean}
49690      */
49691     hasSelection : function(){
49692         return this.selection ? true : false;
49693     },
49694
49695     /** @ignore */
49696     handleMouseDown : function(e, t){
49697         var v = this.grid.getView();
49698         if(this.isLocked()){
49699             return;
49700         };
49701         var row = v.findRowIndex(t);
49702         var cell = v.findCellIndex(t);
49703         if(row !== false && cell !== false){
49704             this.select(row, cell);
49705         }
49706     },
49707
49708     /**
49709      * Selects a cell.
49710      * @param {Number} rowIndex
49711      * @param {Number} collIndex
49712      */
49713     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
49714         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
49715             this.clearSelections();
49716             r = r || this.grid.dataSource.getAt(rowIndex);
49717             this.selection = {
49718                 record : r,
49719                 cell : [rowIndex, colIndex]
49720             };
49721             if(!preventViewNotify){
49722                 var v = this.grid.getView();
49723                 v.onCellSelect(rowIndex, colIndex);
49724                 if(preventFocus !== true){
49725                     v.focusCell(rowIndex, colIndex);
49726                 }
49727             }
49728             this.fireEvent("cellselect", this, rowIndex, colIndex);
49729             this.fireEvent("selectionchange", this, this.selection);
49730         }
49731     },
49732
49733         //private
49734     isSelectable : function(rowIndex, colIndex, cm){
49735         return !cm.isHidden(colIndex);
49736     },
49737
49738     /** @ignore */
49739     handleKeyDown : function(e){
49740         Roo.log('Cell Sel Model handleKeyDown');
49741         if(!e.isNavKeyPress()){
49742             return;
49743         }
49744         var g = this.grid, s = this.selection;
49745         if(!s){
49746             e.stopEvent();
49747             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
49748             if(cell){
49749                 this.select(cell[0], cell[1]);
49750             }
49751             return;
49752         }
49753         var sm = this;
49754         var walk = function(row, col, step){
49755             return g.walkCells(row, col, step, sm.isSelectable,  sm);
49756         };
49757         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
49758         var newCell;
49759
49760         switch(k){
49761             case e.TAB:
49762                 // handled by onEditorKey
49763                 if (g.isEditor && g.editing) {
49764                     return;
49765                 }
49766                 if(e.shiftKey){
49767                      newCell = walk(r, c-1, -1);
49768                 }else{
49769                      newCell = walk(r, c+1, 1);
49770                 }
49771              break;
49772              case e.DOWN:
49773                  newCell = walk(r+1, c, 1);
49774              break;
49775              case e.UP:
49776                  newCell = walk(r-1, c, -1);
49777              break;
49778              case e.RIGHT:
49779                  newCell = walk(r, c+1, 1);
49780              break;
49781              case e.LEFT:
49782                  newCell = walk(r, c-1, -1);
49783              break;
49784              case e.ENTER:
49785                  if(g.isEditor && !g.editing){
49786                     g.startEditing(r, c);
49787                     e.stopEvent();
49788                     return;
49789                 }
49790              break;
49791         };
49792         if(newCell){
49793             this.select(newCell[0], newCell[1]);
49794             e.stopEvent();
49795         }
49796     },
49797
49798     acceptsNav : function(row, col, cm){
49799         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49800     },
49801
49802     onEditorKey : function(field, e){
49803         
49804         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49805         ///Roo.log('onEditorKey' + k);
49806         
49807         if(k == e.TAB){
49808             if(e.shiftKey){
49809                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49810             }else{
49811                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49812             }
49813             e.stopEvent();
49814         }else if(k == e.ENTER && !e.ctrlKey){
49815             ed.completeEdit();
49816             e.stopEvent();
49817             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49818         }else if(k == e.ESC){
49819             ed.cancelEdit();
49820         }
49821         
49822         
49823         if(newCell){
49824             //Roo.log('next cell after edit');
49825             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
49826         }
49827     }
49828 });/*
49829  * Based on:
49830  * Ext JS Library 1.1.1
49831  * Copyright(c) 2006-2007, Ext JS, LLC.
49832  *
49833  * Originally Released Under LGPL - original licence link has changed is not relivant.
49834  *
49835  * Fork - LGPL
49836  * <script type="text/javascript">
49837  */
49838  
49839 /**
49840  * @class Roo.grid.EditorGrid
49841  * @extends Roo.grid.Grid
49842  * Class for creating and editable grid.
49843  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
49844  * The container MUST have some type of size defined for the grid to fill. The container will be 
49845  * automatically set to position relative if it isn't already.
49846  * @param {Object} dataSource The data model to bind to
49847  * @param {Object} colModel The column model with info about this grid's columns
49848  */
49849 Roo.grid.EditorGrid = function(container, config){
49850     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
49851     this.getGridEl().addClass("xedit-grid");
49852
49853     if(!this.selModel){
49854         this.selModel = new Roo.grid.CellSelectionModel();
49855     }
49856
49857     this.activeEditor = null;
49858
49859         this.addEvents({
49860             /**
49861              * @event beforeedit
49862              * Fires before cell editing is triggered. The edit event object has the following properties <br />
49863              * <ul style="padding:5px;padding-left:16px;">
49864              * <li>grid - This grid</li>
49865              * <li>record - The record being edited</li>
49866              * <li>field - The field name being edited</li>
49867              * <li>value - The value for the field being edited.</li>
49868              * <li>row - The grid row index</li>
49869              * <li>column - The grid column index</li>
49870              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49871              * </ul>
49872              * @param {Object} e An edit event (see above for description)
49873              */
49874             "beforeedit" : true,
49875             /**
49876              * @event afteredit
49877              * Fires after a cell is edited. <br />
49878              * <ul style="padding:5px;padding-left:16px;">
49879              * <li>grid - This grid</li>
49880              * <li>record - The record being edited</li>
49881              * <li>field - The field name being edited</li>
49882              * <li>value - The value being set</li>
49883              * <li>originalValue - The original value for the field, before the edit.</li>
49884              * <li>row - The grid row index</li>
49885              * <li>column - The grid column index</li>
49886              * </ul>
49887              * @param {Object} e An edit event (see above for description)
49888              */
49889             "afteredit" : true,
49890             /**
49891              * @event validateedit
49892              * Fires after a cell is edited, but before the value is set in the record. 
49893          * You can use this to modify the value being set in the field, Return false
49894              * to cancel the change. The edit event object has the following properties <br />
49895              * <ul style="padding:5px;padding-left:16px;">
49896          * <li>editor - This editor</li>
49897              * <li>grid - This grid</li>
49898              * <li>record - The record being edited</li>
49899              * <li>field - The field name being edited</li>
49900              * <li>value - The value being set</li>
49901              * <li>originalValue - The original value for the field, before the edit.</li>
49902              * <li>row - The grid row index</li>
49903              * <li>column - The grid column index</li>
49904              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49905              * </ul>
49906              * @param {Object} e An edit event (see above for description)
49907              */
49908             "validateedit" : true
49909         });
49910     this.on("bodyscroll", this.stopEditing,  this);
49911     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
49912 };
49913
49914 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
49915     /**
49916      * @cfg {Number} clicksToEdit
49917      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
49918      */
49919     clicksToEdit: 2,
49920
49921     // private
49922     isEditor : true,
49923     // private
49924     trackMouseOver: false, // causes very odd FF errors
49925
49926     onCellDblClick : function(g, row, col){
49927         this.startEditing(row, col);
49928     },
49929
49930     onEditComplete : function(ed, value, startValue){
49931         this.editing = false;
49932         this.activeEditor = null;
49933         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
49934         var r = ed.record;
49935         var field = this.colModel.getDataIndex(ed.col);
49936         var e = {
49937             grid: this,
49938             record: r,
49939             field: field,
49940             originalValue: startValue,
49941             value: value,
49942             row: ed.row,
49943             column: ed.col,
49944             cancel:false,
49945             editor: ed
49946         };
49947         if(String(value) !== String(startValue)){
49948             
49949             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
49950                 r.set(field, e.value);
49951                 // if we are dealing with a combo box..
49952                 // then we also set the 'name' colum to be the displayField
49953                 if (ed.field.displayField && ed.field.name) {
49954                     r.set(ed.field.name, ed.field.el.dom.value);
49955                 }
49956                 
49957                 delete e.cancel; //?? why!!!
49958                 this.fireEvent("afteredit", e);
49959             }
49960         } else {
49961             this.fireEvent("afteredit", e); // always fire it!
49962         }
49963         this.view.focusCell(ed.row, ed.col);
49964     },
49965
49966     /**
49967      * Starts editing the specified for the specified row/column
49968      * @param {Number} rowIndex
49969      * @param {Number} colIndex
49970      */
49971     startEditing : function(row, col){
49972         this.stopEditing();
49973         if(this.colModel.isCellEditable(col, row)){
49974             this.view.ensureVisible(row, col, true);
49975             var r = this.dataSource.getAt(row);
49976             var field = this.colModel.getDataIndex(col);
49977             var e = {
49978                 grid: this,
49979                 record: r,
49980                 field: field,
49981                 value: r.data[field],
49982                 row: row,
49983                 column: col,
49984                 cancel:false
49985             };
49986             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
49987                 this.editing = true;
49988                 var ed = this.colModel.getCellEditor(col, row);
49989                 
49990                 if (!ed) {
49991                     return;
49992                 }
49993                 if(!ed.rendered){
49994                     ed.render(ed.parentEl || document.body);
49995                 }
49996                 ed.field.reset();
49997                 (function(){ // complex but required for focus issues in safari, ie and opera
49998                     ed.row = row;
49999                     ed.col = col;
50000                     ed.record = r;
50001                     ed.on("complete", this.onEditComplete, this, {single: true});
50002                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50003                     this.activeEditor = ed;
50004                     var v = r.data[field];
50005                     ed.startEdit(this.view.getCell(row, col), v);
50006                     // combo's with 'displayField and name set
50007                     if (ed.field.displayField && ed.field.name) {
50008                         ed.field.el.dom.value = r.data[ed.field.name];
50009                     }
50010                     
50011                     
50012                 }).defer(50, this);
50013             }
50014         }
50015     },
50016         
50017     /**
50018      * Stops any active editing
50019      */
50020     stopEditing : function(){
50021         if(this.activeEditor){
50022             this.activeEditor.completeEdit();
50023         }
50024         this.activeEditor = null;
50025     }
50026 });/*
50027  * Based on:
50028  * Ext JS Library 1.1.1
50029  * Copyright(c) 2006-2007, Ext JS, LLC.
50030  *
50031  * Originally Released Under LGPL - original licence link has changed is not relivant.
50032  *
50033  * Fork - LGPL
50034  * <script type="text/javascript">
50035  */
50036
50037 // private - not really -- you end up using it !
50038 // This is a support class used internally by the Grid components
50039
50040 /**
50041  * @class Roo.grid.GridEditor
50042  * @extends Roo.Editor
50043  * Class for creating and editable grid elements.
50044  * @param {Object} config any settings (must include field)
50045  */
50046 Roo.grid.GridEditor = function(field, config){
50047     if (!config && field.field) {
50048         config = field;
50049         field = Roo.factory(config.field, Roo.form);
50050     }
50051     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50052     field.monitorTab = false;
50053 };
50054
50055 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50056     
50057     /**
50058      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50059      */
50060     
50061     alignment: "tl-tl",
50062     autoSize: "width",
50063     hideEl : false,
50064     cls: "x-small-editor x-grid-editor",
50065     shim:false,
50066     shadow:"frame"
50067 });/*
50068  * Based on:
50069  * Ext JS Library 1.1.1
50070  * Copyright(c) 2006-2007, Ext JS, LLC.
50071  *
50072  * Originally Released Under LGPL - original licence link has changed is not relivant.
50073  *
50074  * Fork - LGPL
50075  * <script type="text/javascript">
50076  */
50077   
50078
50079   
50080 Roo.grid.PropertyRecord = Roo.data.Record.create([
50081     {name:'name',type:'string'},  'value'
50082 ]);
50083
50084
50085 Roo.grid.PropertyStore = function(grid, source){
50086     this.grid = grid;
50087     this.store = new Roo.data.Store({
50088         recordType : Roo.grid.PropertyRecord
50089     });
50090     this.store.on('update', this.onUpdate,  this);
50091     if(source){
50092         this.setSource(source);
50093     }
50094     Roo.grid.PropertyStore.superclass.constructor.call(this);
50095 };
50096
50097
50098
50099 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50100     setSource : function(o){
50101         this.source = o;
50102         this.store.removeAll();
50103         var data = [];
50104         for(var k in o){
50105             if(this.isEditableValue(o[k])){
50106                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50107             }
50108         }
50109         this.store.loadRecords({records: data}, {}, true);
50110     },
50111
50112     onUpdate : function(ds, record, type){
50113         if(type == Roo.data.Record.EDIT){
50114             var v = record.data['value'];
50115             var oldValue = record.modified['value'];
50116             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50117                 this.source[record.id] = v;
50118                 record.commit();
50119                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50120             }else{
50121                 record.reject();
50122             }
50123         }
50124     },
50125
50126     getProperty : function(row){
50127        return this.store.getAt(row);
50128     },
50129
50130     isEditableValue: function(val){
50131         if(val && val instanceof Date){
50132             return true;
50133         }else if(typeof val == 'object' || typeof val == 'function'){
50134             return false;
50135         }
50136         return true;
50137     },
50138
50139     setValue : function(prop, value){
50140         this.source[prop] = value;
50141         this.store.getById(prop).set('value', value);
50142     },
50143
50144     getSource : function(){
50145         return this.source;
50146     }
50147 });
50148
50149 Roo.grid.PropertyColumnModel = function(grid, store){
50150     this.grid = grid;
50151     var g = Roo.grid;
50152     g.PropertyColumnModel.superclass.constructor.call(this, [
50153         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50154         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50155     ]);
50156     this.store = store;
50157     this.bselect = Roo.DomHelper.append(document.body, {
50158         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50159             {tag: 'option', value: 'true', html: 'true'},
50160             {tag: 'option', value: 'false', html: 'false'}
50161         ]
50162     });
50163     Roo.id(this.bselect);
50164     var f = Roo.form;
50165     this.editors = {
50166         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50167         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50168         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50169         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50170         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50171     };
50172     this.renderCellDelegate = this.renderCell.createDelegate(this);
50173     this.renderPropDelegate = this.renderProp.createDelegate(this);
50174 };
50175
50176 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50177     
50178     
50179     nameText : 'Name',
50180     valueText : 'Value',
50181     
50182     dateFormat : 'm/j/Y',
50183     
50184     
50185     renderDate : function(dateVal){
50186         return dateVal.dateFormat(this.dateFormat);
50187     },
50188
50189     renderBool : function(bVal){
50190         return bVal ? 'true' : 'false';
50191     },
50192
50193     isCellEditable : function(colIndex, rowIndex){
50194         return colIndex == 1;
50195     },
50196
50197     getRenderer : function(col){
50198         return col == 1 ?
50199             this.renderCellDelegate : this.renderPropDelegate;
50200     },
50201
50202     renderProp : function(v){
50203         return this.getPropertyName(v);
50204     },
50205
50206     renderCell : function(val){
50207         var rv = val;
50208         if(val instanceof Date){
50209             rv = this.renderDate(val);
50210         }else if(typeof val == 'boolean'){
50211             rv = this.renderBool(val);
50212         }
50213         return Roo.util.Format.htmlEncode(rv);
50214     },
50215
50216     getPropertyName : function(name){
50217         var pn = this.grid.propertyNames;
50218         return pn && pn[name] ? pn[name] : name;
50219     },
50220
50221     getCellEditor : function(colIndex, rowIndex){
50222         var p = this.store.getProperty(rowIndex);
50223         var n = p.data['name'], val = p.data['value'];
50224         
50225         if(typeof(this.grid.customEditors[n]) == 'string'){
50226             return this.editors[this.grid.customEditors[n]];
50227         }
50228         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50229             return this.grid.customEditors[n];
50230         }
50231         if(val instanceof Date){
50232             return this.editors['date'];
50233         }else if(typeof val == 'number'){
50234             return this.editors['number'];
50235         }else if(typeof val == 'boolean'){
50236             return this.editors['boolean'];
50237         }else{
50238             return this.editors['string'];
50239         }
50240     }
50241 });
50242
50243 /**
50244  * @class Roo.grid.PropertyGrid
50245  * @extends Roo.grid.EditorGrid
50246  * This class represents the  interface of a component based property grid control.
50247  * <br><br>Usage:<pre><code>
50248  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50249       
50250  });
50251  // set any options
50252  grid.render();
50253  * </code></pre>
50254   
50255  * @constructor
50256  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50257  * The container MUST have some type of size defined for the grid to fill. The container will be
50258  * automatically set to position relative if it isn't already.
50259  * @param {Object} config A config object that sets properties on this grid.
50260  */
50261 Roo.grid.PropertyGrid = function(container, config){
50262     config = config || {};
50263     var store = new Roo.grid.PropertyStore(this);
50264     this.store = store;
50265     var cm = new Roo.grid.PropertyColumnModel(this, store);
50266     store.store.sort('name', 'ASC');
50267     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50268         ds: store.store,
50269         cm: cm,
50270         enableColLock:false,
50271         enableColumnMove:false,
50272         stripeRows:false,
50273         trackMouseOver: false,
50274         clicksToEdit:1
50275     }, config));
50276     this.getGridEl().addClass('x-props-grid');
50277     this.lastEditRow = null;
50278     this.on('columnresize', this.onColumnResize, this);
50279     this.addEvents({
50280          /**
50281              * @event beforepropertychange
50282              * Fires before a property changes (return false to stop?)
50283              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50284              * @param {String} id Record Id
50285              * @param {String} newval New Value
50286          * @param {String} oldval Old Value
50287              */
50288         "beforepropertychange": true,
50289         /**
50290              * @event propertychange
50291              * Fires after a property changes
50292              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50293              * @param {String} id Record Id
50294              * @param {String} newval New Value
50295          * @param {String} oldval Old Value
50296              */
50297         "propertychange": true
50298     });
50299     this.customEditors = this.customEditors || {};
50300 };
50301 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50302     
50303      /**
50304      * @cfg {Object} customEditors map of colnames=> custom editors.
50305      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50306      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50307      * false disables editing of the field.
50308          */
50309     
50310       /**
50311      * @cfg {Object} propertyNames map of property Names to their displayed value
50312          */
50313     
50314     render : function(){
50315         Roo.grid.PropertyGrid.superclass.render.call(this);
50316         this.autoSize.defer(100, this);
50317     },
50318
50319     autoSize : function(){
50320         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50321         if(this.view){
50322             this.view.fitColumns();
50323         }
50324     },
50325
50326     onColumnResize : function(){
50327         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50328         this.autoSize();
50329     },
50330     /**
50331      * Sets the data for the Grid
50332      * accepts a Key => Value object of all the elements avaiable.
50333      * @param {Object} data  to appear in grid.
50334      */
50335     setSource : function(source){
50336         this.store.setSource(source);
50337         //this.autoSize();
50338     },
50339     /**
50340      * Gets all the data from the grid.
50341      * @return {Object} data  data stored in grid
50342      */
50343     getSource : function(){
50344         return this.store.getSource();
50345     }
50346 });/*
50347  * Based on:
50348  * Ext JS Library 1.1.1
50349  * Copyright(c) 2006-2007, Ext JS, LLC.
50350  *
50351  * Originally Released Under LGPL - original licence link has changed is not relivant.
50352  *
50353  * Fork - LGPL
50354  * <script type="text/javascript">
50355  */
50356  
50357 /**
50358  * @class Roo.LoadMask
50359  * A simple utility class for generically masking elements while loading data.  If the element being masked has
50360  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
50361  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
50362  * element's UpdateManager load indicator and will be destroyed after the initial load.
50363  * @constructor
50364  * Create a new LoadMask
50365  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
50366  * @param {Object} config The config object
50367  */
50368 Roo.LoadMask = function(el, config){
50369     this.el = Roo.get(el);
50370     Roo.apply(this, config);
50371     if(this.store){
50372         this.store.on('beforeload', this.onBeforeLoad, this);
50373         this.store.on('load', this.onLoad, this);
50374         this.store.on('loadexception', this.onLoad, this);
50375         this.removeMask = false;
50376     }else{
50377         var um = this.el.getUpdateManager();
50378         um.showLoadIndicator = false; // disable the default indicator
50379         um.on('beforeupdate', this.onBeforeLoad, this);
50380         um.on('update', this.onLoad, this);
50381         um.on('failure', this.onLoad, this);
50382         this.removeMask = true;
50383     }
50384 };
50385
50386 Roo.LoadMask.prototype = {
50387     /**
50388      * @cfg {Boolean} removeMask
50389      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
50390      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
50391      */
50392     /**
50393      * @cfg {String} msg
50394      * The text to display in a centered loading message box (defaults to 'Loading...')
50395      */
50396     msg : 'Loading...',
50397     /**
50398      * @cfg {String} msgCls
50399      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
50400      */
50401     msgCls : 'x-mask-loading',
50402
50403     /**
50404      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
50405      * @type Boolean
50406      */
50407     disabled: false,
50408
50409     /**
50410      * Disables the mask to prevent it from being displayed
50411      */
50412     disable : function(){
50413        this.disabled = true;
50414     },
50415
50416     /**
50417      * Enables the mask so that it can be displayed
50418      */
50419     enable : function(){
50420         this.disabled = false;
50421     },
50422
50423     // private
50424     onLoad : function(){
50425         this.el.unmask(this.removeMask);
50426     },
50427
50428     // private
50429     onBeforeLoad : function(){
50430         if(!this.disabled){
50431             this.el.mask(this.msg, this.msgCls);
50432         }
50433     },
50434
50435     // private
50436     destroy : function(){
50437         if(this.store){
50438             this.store.un('beforeload', this.onBeforeLoad, this);
50439             this.store.un('load', this.onLoad, this);
50440             this.store.un('loadexception', this.onLoad, this);
50441         }else{
50442             var um = this.el.getUpdateManager();
50443             um.un('beforeupdate', this.onBeforeLoad, this);
50444             um.un('update', this.onLoad, this);
50445             um.un('failure', this.onLoad, this);
50446         }
50447     }
50448 };/*
50449  * Based on:
50450  * Ext JS Library 1.1.1
50451  * Copyright(c) 2006-2007, Ext JS, LLC.
50452  *
50453  * Originally Released Under LGPL - original licence link has changed is not relivant.
50454  *
50455  * Fork - LGPL
50456  * <script type="text/javascript">
50457  */
50458 Roo.XTemplate = function(){
50459     Roo.XTemplate.superclass.constructor.apply(this, arguments);
50460     var s = this.html;
50461
50462     s = ['<tpl>', s, '</tpl>'].join('');
50463
50464     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
50465
50466     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
50467     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
50468     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
50469     var m, id = 0;
50470     var tpls = [];
50471
50472     while(m = s.match(re)){
50473        var m2 = m[0].match(nameRe);
50474        var m3 = m[0].match(ifRe);
50475        var m4 = m[0].match(execRe);
50476        var exp = null, fn = null, exec = null;
50477        var name = m2 && m2[1] ? m2[1] : '';
50478        if(m3){
50479            exp = m3 && m3[1] ? m3[1] : null;
50480            if(exp){
50481                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
50482            }
50483        }
50484        if(m4){
50485            exp = m4 && m4[1] ? m4[1] : null;
50486            if(exp){
50487                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
50488            }
50489        }
50490        if(name){
50491            switch(name){
50492                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
50493                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
50494                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
50495            }
50496        }
50497        tpls.push({
50498             id: id,
50499             target: name,
50500             exec: exec,
50501             test: fn,
50502             body: m[1]||''
50503         });
50504        s = s.replace(m[0], '{xtpl'+ id + '}');
50505        ++id;
50506     }
50507     for(var i = tpls.length-1; i >= 0; --i){
50508         this.compileTpl(tpls[i]);
50509     }
50510     this.master = tpls[tpls.length-1];
50511     this.tpls = tpls;
50512 };
50513 Roo.extend(Roo.XTemplate, Roo.Template, {
50514
50515     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
50516
50517     applySubTemplate : function(id, values, parent){
50518         var t = this.tpls[id];
50519         if(t.test && !t.test.call(this, values, parent)){
50520             return '';
50521         }
50522         if(t.exec && t.exec.call(this, values, parent)){
50523             return '';
50524         }
50525         var vs = t.target ? t.target.call(this, values, parent) : values;
50526         parent = t.target ? values : parent;
50527         if(t.target && vs instanceof Array){
50528             var buf = [];
50529             for(var i = 0, len = vs.length; i < len; i++){
50530                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
50531             }
50532             return buf.join('');
50533         }
50534         return t.compiled.call(this, vs, parent);
50535     },
50536
50537     compileTpl : function(tpl){
50538         var fm = Roo.util.Format;
50539         var useF = this.disableFormats !== true;
50540         var sep = Roo.isGecko ? "+" : ",";
50541         var fn = function(m, name, format, args){
50542             if(name.substr(0, 4) == 'xtpl'){
50543                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
50544             }
50545             var v;
50546             if(name.indexOf('.') != -1){
50547                 v = name;
50548             }else{
50549                 v = "values['" + name + "']";
50550             }
50551             if(format && useF){
50552                 args = args ? ',' + args : "";
50553                 if(format.substr(0, 5) != "this."){
50554                     format = "fm." + format + '(';
50555                 }else{
50556                     format = 'this.call("'+ format.substr(5) + '", ';
50557                     args = ", values";
50558                 }
50559             }else{
50560                 args= ''; format = "("+v+" === undefined ? '' : ";
50561             }
50562             return "'"+ sep + format + v + args + ")"+sep+"'";
50563         };
50564         var body;
50565         // branched to use + in gecko and [].join() in others
50566         if(Roo.isGecko){
50567             body = "tpl.compiled = function(values, parent){ return '" +
50568                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
50569                     "';};";
50570         }else{
50571             body = ["tpl.compiled = function(values, parent){ return ['"];
50572             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
50573             body.push("'].join('');};");
50574             body = body.join('');
50575         }
50576         /** eval:var:zzzzzzz */
50577         eval(body);
50578         return this;
50579     },
50580
50581     applyTemplate : function(values){
50582         return this.master.compiled.call(this, values, {});
50583         var s = this.subs;
50584     },
50585
50586     apply : function(){
50587         return this.applyTemplate.apply(this, arguments);
50588     },
50589
50590     compile : function(){return this;}
50591 });
50592
50593 Roo.XTemplate.from = function(el){
50594     el = Roo.getDom(el);
50595     return new Roo.XTemplate(el.value || el.innerHTML);
50596 };/*
50597  * Original code for Roojs - LGPL
50598  * <script type="text/javascript">
50599  */
50600  
50601 /**
50602  * @class Roo.XComponent
50603  * A delayed Element creator...
50604  * 
50605  * Mypart.xyx = new Roo.XComponent({
50606
50607     parent : 'Mypart.xyz', // empty == document.element.!!
50608     order : '001',
50609     name : 'xxxx'
50610     region : 'xxxx'
50611     disabled : function() {} 
50612      
50613     tree : function() { // return an tree of xtype declared components
50614         var MODULE = this;
50615         return 
50616         {
50617             xtype : 'NestedLayoutPanel',
50618             // technicall
50619         }
50620      ]
50621  *})
50622  * @extends Roo.util.Observable
50623  * @constructor
50624  * @param cfg {Object} configuration of component
50625  * 
50626  */
50627 Roo.XComponent = function(cfg) {
50628     Roo.apply(this, cfg);
50629     this.addEvents({ 
50630         /**
50631              * @event built
50632              * Fires when this the componnt is built
50633              * @param {Roo.XComponent} c the component
50634              */
50635         'built' : true,
50636         /**
50637              * @event buildcomplete
50638              * Fires on the top level element when all elements have been built
50639              * @param {Roo.XComponent} c the top level component.
50640          */
50641         'buildcomplete' : true
50642         
50643     });
50644     
50645     Roo.XComponent.register(this);
50646     this.modules = false;
50647     this.el = false; // where the layout goes..
50648     
50649     
50650 }
50651 Roo.extend(Roo.XComponent, Roo.util.Observable, {
50652     /**
50653      * @property el
50654      * The created element (with Roo.factory())
50655      * @type {Roo.Layout}
50656      */
50657     el  : false,
50658     
50659     /**
50660      * @property el
50661      * for BC  - use el in new code
50662      * @type {Roo.Layout}
50663      */
50664     panel : false,
50665     
50666     /**
50667      * @property layout
50668      * for BC  - use el in new code
50669      * @type {Roo.Layout}
50670      */
50671     layout : false,
50672     
50673      /**
50674      * @cfg {Function|boolean} disabled
50675      * If this module is disabled by some rule, return true from the funtion
50676      */
50677     disabled : false,
50678     
50679     /**
50680      * @cfg {String} parent 
50681      * Name of parent element which it get xtype added to..
50682      */
50683     parent: false,
50684     
50685     /**
50686      * @cfg {String} order
50687      * Used to set the order in which elements are created (usefull for multiple tabs)
50688      */
50689     
50690     order : false,
50691     /**
50692      * @cfg {String} name
50693      * String to display while loading.
50694      */
50695     name : false,
50696     /**
50697      * @cfg {Array} items
50698      * A single item array - the first element is the root of the tree..
50699      * It's done this way to stay compatible with the Xtype system...
50700      */
50701     items : false
50702      
50703      
50704     
50705 });
50706
50707 Roo.apply(Roo.XComponent, {
50708     
50709     /**
50710      * @property  buildCompleted
50711      * True when the builder has completed building the interface.
50712      * @type Boolean
50713      */
50714     buildCompleted : false,
50715      
50716     /**
50717      * @property  topModule
50718      * the upper most module - uses document.element as it's constructor.
50719      * @type Object
50720      */
50721      
50722     topModule  : false,
50723       
50724     /**
50725      * @property  modules
50726      * array of modules to be created by registration system.
50727      * @type Roo.XComponent
50728      */
50729     
50730     modules : [],
50731       
50732     
50733     /**
50734      * Register components to be built later.
50735      *
50736      * This solves the following issues
50737      * - Building is not done on page load, but after an authentication process has occured.
50738      * - Interface elements are registered on page load
50739      * - Parent Interface elements may not be loaded before child, so this handles that..
50740      * 
50741      *
50742      * example:
50743      * 
50744      * MyApp.register({
50745           order : '000001',
50746           module : 'Pman.Tab.projectMgr',
50747           region : 'center',
50748           parent : 'Pman.layout',
50749           disabled : false,  // or use a function..
50750         })
50751      
50752      * * @param {Object} details about module
50753      */
50754     register : function(obj) {
50755         this.modules.push(obj);
50756          
50757     },
50758     /**
50759      * convert a string to an object..
50760      * 
50761      */
50762     
50763     toObject : function(str)
50764     {
50765         if (!str || typeof(str) == 'object') {
50766             return str;
50767         }
50768         var ar = str.split('.');
50769         var rt, o;
50770         rt = ar.shift();
50771             /** eval:var:o */
50772         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
50773         if (o === false) {
50774             throw "Module not found : " + str;
50775         }
50776         Roo.each(ar, function(e) {
50777             if (typeof(o[e]) == 'undefined') {
50778                 throw "Module not found : " + str;
50779             }
50780             o = o[e];
50781         });
50782         return o;
50783         
50784     },
50785     
50786     
50787     /**
50788      * move modules into their correct place in the tree..
50789      * 
50790      */
50791     preBuild : function ()
50792     {
50793         
50794         Roo.each(this.modules , function (obj)
50795         {
50796             obj.parent = this.toObject(obj.parent);
50797             
50798             if (!obj.parent) {
50799                 this.topModule = obj;
50800                 return;
50801             }
50802             
50803             if (!obj.parent.modules) {
50804                 obj.parent.modules = new Roo.util.MixedCollection(false, 
50805                     function(o) { return o.order + '' }
50806                 );
50807             }
50808             
50809             obj.parent.modules.add(obj);
50810         }, this);
50811     },
50812     
50813      /**
50814      * make a list of modules to build.
50815      * @return {Array} list of modules. 
50816      */ 
50817     
50818     buildOrder : function()
50819     {
50820         var _this = this;
50821         var cmp = function(a,b) {   
50822             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
50823         };
50824         
50825         if (!this.topModule || !this.topModule.modules) {
50826             throw "No top level modules to build";
50827         }
50828        
50829         // make a flat list in order of modules to build.
50830         var mods = [ this.topModule ];
50831         
50832         
50833         // add modules to their parents..
50834         var addMod = function(m) {
50835            // Roo.debug && Roo.log(m.modKey);
50836             
50837             mods.push(m);
50838             if (m.modules) {
50839                 m.modules.keySort('ASC',  cmp );
50840                 m.modules.each(addMod);
50841             }
50842             // not sure if this is used any more..
50843             if (m.finalize) {
50844                 m.finalize.name = m.name + " (clean up) ";
50845                 mods.push(m.finalize);
50846             }
50847             
50848         }
50849         this.topModule.modules.keySort('ASC',  cmp );
50850         this.topModule.modules.each(addMod);
50851         return mods;
50852     },
50853     
50854      /**
50855      * Build the registered modules.
50856      * @param {Object} parent element.
50857      * @param {Function} optional method to call after module has been added.
50858      * 
50859      */ 
50860    
50861     build : function() 
50862     {
50863         
50864         this.preBuild();
50865         var mods = this.buildOrder();
50866       
50867         //this.allmods = mods;
50868         //Roo.debug && Roo.log(mods);
50869         //return;
50870         if (!mods.length) { // should not happen
50871             throw "NO modules!!!";
50872         }
50873         
50874         
50875         
50876         // flash it up as modal - so we store the mask!?
50877         Roo.MessageBox.show({ title: 'loading' });
50878         Roo.MessageBox.show({
50879            title: "Please wait...",
50880            msg: "Building Interface...",
50881            width:450,
50882            progress:true,
50883            closable:false,
50884            modal: false
50885           
50886         });
50887         var total = mods.length;
50888         
50889         var _this = this;
50890         var progressRun = function() {
50891             if (!mods.length) {
50892                 Roo.debug && Roo.log('hide?');
50893                 Roo.MessageBox.hide();
50894                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
50895                 return;    
50896             }
50897             
50898             var m = mods.shift();
50899             Roo.debug && Roo.log(m);
50900             if (typeof(m) == 'function') { // not sure if this is supported any more..
50901                 m.call(this);
50902                 return progressRun.defer(10, _this);
50903             } 
50904             
50905             Roo.MessageBox.updateProgress(
50906                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
50907                     " of " + total + 
50908                     (m.name ? (' - ' + m.name) : '')
50909                     );
50910             
50911          
50912             
50913             var disabled = (typeof(m.disabled) == 'function') ?
50914                 m.disabled.call(m.module.disabled) : m.disabled;    
50915             
50916             
50917             if (disabled) {
50918                 return progressRun(); // we do not update the display!
50919             }
50920             
50921             if (!m.parent) {
50922                 // it's a top level one..
50923                 var layoutbase = new Ext.BorderLayout(document.body, {
50924                
50925                     center: {
50926                          titlebar: false,
50927                          autoScroll:false,
50928                          closeOnTab: true,
50929                          tabPosition: 'top',
50930                          //resizeTabs: true,
50931                          alwaysShowTabs: true,
50932                          minTabWidth: 140
50933                     }
50934                 });
50935                 var tree = m.tree();
50936                 tree.region = 'center';
50937                 m.el = layoutbase.addxtype(tree);
50938                 m.panel = m.el;
50939                 m.layout = m.panel.layout;    
50940                 return progressRun.defer(10, _this);
50941             }
50942             
50943             var tree = m.tree();
50944             tree.region = tree.region || m.region;
50945             m.el = m.parent.el.addxtype(tree);
50946             m.fireEvent('built', m);
50947             m.panel = m.el;
50948             m.layout = m.panel.layout;    
50949             progressRun.defer(10, _this); 
50950             
50951         }
50952         progressRun.defer(1, _this);
50953      
50954         
50955         
50956     }
50957      
50958    
50959     
50960     
50961 });
50962  //<script type="text/javascript">
50963
50964
50965 /**
50966  * @class Roo.Login
50967  * @extends Roo.LayoutDialog
50968  * A generic Login Dialog..... - only one needed in theory!?!?
50969  *
50970  * Fires XComponent builder on success...
50971  * 
50972  * Sends 
50973  *    username,password, lang = for login actions.
50974  *    check = 1 for periodic checking that sesion is valid.
50975  *    passwordRequest = email request password
50976  *    logout = 1 = to logout
50977  * 
50978  * Affects: (this id="????" elements)
50979  *   loading  (removed) (used to indicate application is loading)
50980  *   loading-mask (hides) (used to hide application when it's building loading)
50981  *   
50982  * 
50983  * Usage: 
50984  *    
50985  * 
50986  * Myapp.login = Roo.Login({
50987      url: xxxx,
50988    
50989      realm : 'Myapp', 
50990      
50991      
50992      method : 'POST',
50993      
50994      
50995      * 
50996  })
50997  * 
50998  * 
50999  * 
51000  **/
51001  
51002 Roo.Login = function(cfg)
51003 {
51004     this.addEvents({
51005         'refreshed' : true
51006     });
51007     
51008     Roo.apply(this,cfg);
51009     
51010     Roo.onReady(function() {
51011         this.onLoad();
51012     }, this);
51013     // call parent..
51014     
51015    
51016     Roo.Login.superclass.constructor.call(this, this);
51017     //this.addxtype(this.items[0]);
51018     
51019     
51020 }
51021
51022
51023 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51024     
51025     /**
51026      * @cfg {String} method
51027      * Method used to query for login details.
51028      */
51029     
51030     method : 'POST',
51031     /**
51032      * @cfg {String} url
51033      * URL to query login data. - eg. baseURL + '/Login.php'
51034      */
51035     url : '',
51036     
51037     /**
51038      * @property user
51039      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51040      * @type {Object} 
51041      */
51042     user : false,
51043     /**
51044      * @property checkFails
51045      * Number of times we have attempted to get authentication check, and failed.
51046      * @type {Number} 
51047      */
51048     checkFails : 0,
51049       /**
51050      * @property intervalID
51051      * The window interval that does the constant login checking.
51052      * @type {Number} 
51053      */
51054     intervalID : 0,
51055     
51056     
51057     onLoad : function() // called on page load...
51058     {
51059         // load 
51060          
51061         if (Roo.get('loading')) { // clear any loading indicator..
51062             Roo.get('loading').remove();
51063         }
51064         
51065         //this.switchLang('en'); // set the language to english..
51066        
51067         this.check({
51068             success:  function(response, opts)  {  // check successfull...
51069             
51070                 var res = this.processResponse(response);
51071                 this.checkFails =0;
51072                 if (!res.success) { // error!
51073                     this.checkFails = 5;
51074                     //console.log('call failure');
51075                     return this.failure(response,opts);
51076                 }
51077                 
51078                 if (!res.data.id) { // id=0 == login failure.
51079                     return this.show();
51080                 }
51081                 
51082                               
51083                         //console.log(success);
51084                 this.fillAuth(res.data);   
51085                 this.checkFails =0;
51086                 Roo.XComponent.build();
51087             },
51088             failure : this.show
51089         });
51090         
51091     }, 
51092     
51093     
51094     check: function(cfg) // called every so often to refresh cookie etc..
51095     {
51096         if (cfg.again) { // could be undefined..
51097             this.checkFails++;
51098         } else {
51099             this.checkFails = 0;
51100         }
51101         var _this = this;
51102         if (this.sending) {
51103             if ( this.checkFails > 4) {
51104                 Roo.MessageBox.alert("Error",  
51105                     "Error getting authentication status. - try reloading, or wait a while", function() {
51106                         _this.sending = false;
51107                     }); 
51108                 return;
51109             }
51110             cfg.again = true;
51111             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51112             return;
51113         }
51114         this.sending = true;
51115         
51116         Roo.Ajax.request({  
51117             url: this.url,
51118             params: {
51119                 getAuthUser: true
51120             },  
51121             method: this.method,
51122             success:  cfg.success || this.success,
51123             failure : cfg.failure || this.failure,
51124             scope : this,
51125             callCfg : cfg
51126               
51127         });  
51128     }, 
51129     
51130     
51131     logout: function()
51132     {
51133         window.onbeforeunload = function() { }; // false does not work for IE..
51134         this.user = false;
51135         var _this = this;
51136         
51137         Roo.Ajax.request({  
51138             url: this.url,
51139             params: {
51140                 logout: 1
51141             },  
51142             method: 'GET',
51143             failure : function() {
51144                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51145                     document.location = document.location.toString() + '?ts=' + Math.random();
51146                 });
51147                 
51148             },
51149             success : function() {
51150                 _this.user = false;
51151                 this.checkFails =0;
51152                 // fixme..
51153                 document.location = document.location.toString() + '?ts=' + Math.random();
51154             }
51155               
51156               
51157         }); 
51158     },
51159     
51160     processResponse : function (response)
51161     {
51162         var res = '';
51163         try {
51164             res = Roo.decode(response.responseText);
51165             // oops...
51166             if (typeof(res) != 'object') {
51167                 res = { success : false, errorMsg : res, errors : true };
51168             }
51169             if (typeof(res.success) == 'undefined') {
51170                 res.success = false;
51171             }
51172             
51173         } catch(e) {
51174             res = { success : false,  errorMsg : response.responseText, errors : true };
51175         }
51176         return res;
51177     },
51178     
51179     success : function(response, opts)  // check successfull...
51180     {  
51181         this.sending = false;
51182         var res = this.processResponse(response);
51183         if (!res.success) {
51184             return this.failure(response, opts);
51185         }
51186         if (!res.data || !res.data.id) {
51187             return this.failure(response,opts);
51188         }
51189         //console.log(res);
51190         this.fillAuth(res.data);
51191         
51192         this.checkFails =0;
51193         
51194     },
51195     
51196     
51197     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51198     {
51199         this.authUser = -1;
51200         this.sending = false;
51201         var res = this.processResponse(response);
51202         //console.log(res);
51203         if ( this.checkFails > 2) {
51204         
51205             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51206                 "Error getting authentication status. - try reloading"); 
51207             return;
51208         }
51209         opts.callCfg.again = true;
51210         this.check.defer(1000, this, [ opts.callCfg ]);
51211         return;  
51212     },
51213     
51214     
51215     
51216     fillAuth: function(au) {
51217         this.startAuthCheck();
51218         this.authUserId = au.id;
51219         this.authUser = au;
51220         this.lastChecked = new Date();
51221         this.fireEvent('refreshed', au);
51222         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51223         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51224         au.lang = au.lang || 'en';
51225         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51226         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51227         this.switchLang(au.lang );
51228         
51229      
51230         // open system... - -on setyp..
51231         if (this.authUserId  < 0) {
51232             Roo.MessageBox.alert("Warning", 
51233                 "This is an open system - please set up a admin user with a password.");  
51234         }
51235          
51236         //Pman.onload(); // which should do nothing if it's a re-auth result...
51237         
51238              
51239     },
51240     
51241     startAuthCheck : function() // starter for timeout checking..
51242     {
51243         if (this.intervalID) { // timer already in place...
51244             return false;
51245         }
51246         var _this = this;
51247         this.intervalID =  window.setInterval(function() {
51248               _this.check(false);
51249             }, 120000); // every 120 secs = 2mins..
51250         
51251         
51252     },
51253          
51254     
51255     switchLang : function (lang) 
51256     {
51257         _T = typeof(_T) == 'undefined' ? false : _T;
51258           if (!_T || !lang.length) {
51259             return;
51260         }
51261         
51262         if (!_T && lang != 'en') {
51263             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51264             return;
51265         }
51266         
51267         if (typeof(_T.en) == 'undefined') {
51268             _T.en = {};
51269             Roo.apply(_T.en, _T);
51270         }
51271         
51272         if (typeof(_T[lang]) == 'undefined') {
51273             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51274             return;
51275         }
51276         
51277         
51278         Roo.apply(_T, _T[lang]);
51279         // just need to set the text values for everything...
51280         var _this = this;
51281         /* this will not work ...
51282         if (this.form) { 
51283             
51284                
51285             function formLabel(name, val) {
51286                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
51287             }
51288             
51289             formLabel('password', "Password"+':');
51290             formLabel('username', "Email Address"+':');
51291             formLabel('lang', "Language"+':');
51292             this.dialog.setTitle("Login");
51293             this.dialog.buttons[0].setText("Forgot Password");
51294             this.dialog.buttons[1].setText("Login");
51295         }
51296         */
51297         
51298         
51299     },
51300     
51301     
51302     title: "Login",
51303     modal: true,
51304     width:  350,
51305     //height: 230,
51306     height: 180,
51307     shadow: true,
51308     minWidth:200,
51309     minHeight:180,
51310     //proxyDrag: true,
51311     closable: false,
51312     draggable: false,
51313     collapsible: false,
51314     resizable: false,
51315     center: {  // needed??
51316         autoScroll:false,
51317         titlebar: false,
51318        // tabPosition: 'top',
51319         hideTabs: true,
51320         closeOnTab: true,
51321         alwaysShowTabs: false
51322     } ,
51323     listeners : {
51324         
51325         show  : function(dlg)
51326         {
51327             //console.log(this);
51328             this.form = this.layout.getRegion('center').activePanel.form;
51329             this.form.dialog = dlg;
51330             this.buttons[0].form = this.form;
51331             this.buttons[0].dialog = dlg;
51332             this.buttons[1].form = this.form;
51333             this.buttons[1].dialog = dlg;
51334            
51335            //this.resizeToLogo.defer(1000,this);
51336             // this is all related to resizing for logos..
51337             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
51338            //// if (!sz) {
51339              //   this.resizeToLogo.defer(1000,this);
51340              //   return;
51341            // }
51342             //var w = Ext.lib.Dom.getViewWidth() - 100;
51343             //var h = Ext.lib.Dom.getViewHeight() - 100;
51344             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
51345             //this.center();
51346             if (this.disabled) {
51347                 this.hide();
51348                 return;
51349             }
51350             
51351             if (this.user.id < 0) { // used for inital setup situations.
51352                 return;
51353             }
51354             
51355             if (this.intervalID) {
51356                 // remove the timer
51357                 window.clearInterval(this.intervalID);
51358                 this.intervalID = false;
51359             }
51360             
51361             
51362             if (Roo.get('loading')) {
51363                 Roo.get('loading').remove();
51364             }
51365             if (Roo.get('loading-mask')) {
51366                 Roo.get('loading-mask').hide();
51367             }
51368             
51369             //incomming._node = tnode;
51370             this.form.reset();
51371             //this.dialog.modal = !modal;
51372             //this.dialog.show();
51373             this.el.unmask(); 
51374             
51375             
51376             this.form.setValues({
51377                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
51378                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
51379             });
51380             
51381             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
51382             if (this.form.findField('username').getValue().length > 0 ){
51383                 this.form.findField('password').focus();
51384             } else {
51385                this.form.findField('username').focus();
51386             }
51387     
51388         }
51389     },
51390     items : [
51391          {
51392        
51393             xtype : 'ContentPanel',
51394             xns : Roo,
51395             region: 'center',
51396             fitToFrame : true,
51397             
51398             items : [
51399     
51400                 {
51401                
51402                     xtype : 'Form',
51403                     xns : Roo.form,
51404                     labelWidth: 100,
51405                     style : 'margin: 10px;',
51406                     
51407                     listeners : {
51408                         actionfailed : function(f, act) {
51409                             // form can return { errors: .... }
51410                                 
51411                             //act.result.errors // invalid form element list...
51412                             //act.result.errorMsg// invalid form element list...
51413                             
51414                             this.dialog.el.unmask();
51415                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
51416                                         "Login failed - communication error - try again.");
51417                                       
51418                         },
51419                         actioncomplete: function(re, act) {
51420                              
51421                             Roo.state.Manager.set(
51422                                 this.dialog.realm + '.username',  
51423                                     this.findField('username').getValue()
51424                             );
51425                             Roo.state.Manager.set(
51426                                 this.dialog.realm + '.lang',  
51427                                 this.findField('lang').getValue() 
51428                             );
51429                             
51430                             this.dialog.fillAuth(act.result.data);
51431                               
51432                             this.dialog.hide();
51433                             
51434                             if (Roo.get('loading-mask')) {
51435                                 Roo.get('loading-mask').show();
51436                             }
51437                             Roo.XComponent.build();
51438                             
51439                              
51440                             
51441                         }
51442                     },
51443                     items : [
51444                         {
51445                             xtype : 'TextField',
51446                             xns : Roo.form,
51447                             fieldLabel: "Email Address",
51448                             name: 'username',
51449                             width:200,
51450                             autoCreate : {tag: "input", type: "text", size: "20"}
51451                         },
51452                         {
51453                             xtype : 'TextField',
51454                             xns : Roo.form,
51455                             fieldLabel: "Password",
51456                             inputType: 'password',
51457                             name: 'password',
51458                             width:200,
51459                             autoCreate : {tag: "input", type: "text", size: "20"},
51460                             listeners : {
51461                                 specialkey : function(e,ev) {
51462                                     if (ev.keyCode == 13) {
51463                                         this.form.dialog.el.mask("Logging in");
51464                                         this.form.doAction('submit', {
51465                                             url: this.form.dialog.url,
51466                                             method: this.form.dialog.method
51467                                         });
51468                                     }
51469                                 }
51470                             }  
51471                         },
51472                         {
51473                             xtype : 'ComboBox',
51474                             xns : Roo.form,
51475                             fieldLabel: "Language",
51476                             name : 'langdisp',
51477                             store: {
51478                                 xtype : 'SimpleStore',
51479                                 fields: ['lang', 'ldisp'],
51480                                 data : [
51481                                     [ 'en', 'English' ],
51482                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
51483                                     [ 'zh_CN', '\u7C21\u4E2D' ]
51484                                 ]
51485                             },
51486                             
51487                             valueField : 'lang',
51488                             hiddenName:  'lang',
51489                             width: 200,
51490                             displayField:'ldisp',
51491                             typeAhead: false,
51492                             editable: false,
51493                             mode: 'local',
51494                             triggerAction: 'all',
51495                             emptyText:'Select a Language...',
51496                             selectOnFocus:true,
51497                             listeners : {
51498                                 select :  function(cb, rec, ix) {
51499                                     this.form.switchLang(rec.data.lang);
51500                                 }
51501                             }
51502                         
51503                         }
51504                     ]
51505                 }
51506                   
51507                 
51508             ]
51509         }
51510     ],
51511     buttons : [
51512         {
51513             xtype : 'Button',
51514             xns : 'Roo',
51515             text : "Forgot Password",
51516             listeners : {
51517                 click : function() {
51518                     //console.log(this);
51519                     var n = this.form.findField('username').getValue();
51520                     if (!n.length) {
51521                         Roo.MessageBox.alert("Error", "Fill in your email address");
51522                         return;
51523                     }
51524                     Roo.Ajax.request({
51525                         url: this.dialog.url,
51526                         params: {
51527                             passwordRequest: n
51528                         },
51529                         method: this.dialog.method,
51530                         success:  function(response, opts)  {  // check successfull...
51531                         
51532                             var res = this.dialog.processResponse(response);
51533                             if (!res.success) { // error!
51534                                Roo.MessageBox.alert("Error" ,
51535                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
51536                                return;
51537                             }
51538                             Roo.MessageBox.alert("Notice" ,
51539                                 "Please check you email for the Password Reset message");
51540                         },
51541                         failure : function() {
51542                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
51543                         }
51544                         
51545                     });
51546                 }
51547             }
51548         },
51549         {
51550             xtype : 'Button',
51551             xns : 'Roo',
51552             text : "Login",
51553             listeners : {
51554                 
51555                 click : function () {
51556                         
51557                     this.dialog.el.mask("Logging in");
51558                     this.form.doAction('submit', {
51559                             url: this.dialog.url,
51560                             method: this.dialog.method
51561                     });
51562                 }
51563             }
51564         }
51565     ]
51566   
51567   
51568 })
51569  
51570
51571
51572