Roo.js
[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     '&lt;div name="{id}"&gt;',
4405         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4406     '&lt;/div&gt;'
4407 );
4408 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4409 </code></pre>
4410 * 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>. 
4411 * @constructor
4412 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4413 */
4414 Roo.Template = function(html){
4415     if(html instanceof Array){
4416         html = html.join("");
4417     }else if(arguments.length > 1){
4418         html = Array.prototype.join.call(arguments, "");
4419     }
4420     /**@private*/
4421     this.html = html;
4422     
4423 };
4424 Roo.Template.prototype = {
4425     /**
4426      * Returns an HTML fragment of this template with the specified values applied.
4427      * @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'})
4428      * @return {String} The HTML fragment
4429      */
4430     applyTemplate : function(values){
4431         try {
4432             
4433             if(this.compiled){
4434                 return this.compiled(values);
4435             }
4436             var useF = this.disableFormats !== true;
4437             var fm = Roo.util.Format, tpl = this;
4438             var fn = function(m, name, format, args){
4439                 if(format && useF){
4440                     if(format.substr(0, 5) == "this."){
4441                         return tpl.call(format.substr(5), values[name], values);
4442                     }else{
4443                         if(args){
4444                             // quoted values are required for strings in compiled templates, 
4445                             // but for non compiled we need to strip them
4446                             // quoted reversed for jsmin
4447                             var re = /^\s*['"](.*)["']\s*$/;
4448                             args = args.split(',');
4449                             for(var i = 0, len = args.length; i < len; i++){
4450                                 args[i] = args[i].replace(re, "$1");
4451                             }
4452                             args = [values[name]].concat(args);
4453                         }else{
4454                             args = [values[name]];
4455                         }
4456                         return fm[format].apply(fm, args);
4457                     }
4458                 }else{
4459                     return values[name] !== undefined ? values[name] : "";
4460                 }
4461             };
4462             return this.html.replace(this.re, fn);
4463         } catch (e) {
4464             Roo.log(e);
4465         }
4466         return '';
4467     },
4468     
4469     /**
4470      * Sets the HTML used as the template and optionally compiles it.
4471      * @param {String} html
4472      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4473      * @return {Roo.Template} this
4474      */
4475     set : function(html, compile){
4476         this.html = html;
4477         this.compiled = null;
4478         if(compile){
4479             this.compile();
4480         }
4481         return this;
4482     },
4483     
4484     /**
4485      * True to disable format functions (defaults to false)
4486      * @type Boolean
4487      */
4488     disableFormats : false,
4489     
4490     /**
4491     * The regular expression used to match template variables 
4492     * @type RegExp
4493     * @property 
4494     */
4495     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4496     
4497     /**
4498      * Compiles the template into an internal function, eliminating the RegEx overhead.
4499      * @return {Roo.Template} this
4500      */
4501     compile : function(){
4502         var fm = Roo.util.Format;
4503         var useF = this.disableFormats !== true;
4504         var sep = Roo.isGecko ? "+" : ",";
4505         var fn = function(m, name, format, args){
4506             if(format && useF){
4507                 args = args ? ',' + args : "";
4508                 if(format.substr(0, 5) != "this."){
4509                     format = "fm." + format + '(';
4510                 }else{
4511                     format = 'this.call("'+ format.substr(5) + '", ';
4512                     args = ", values";
4513                 }
4514             }else{
4515                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4516             }
4517             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4518         };
4519         var body;
4520         // branched to use + in gecko and [].join() in others
4521         if(Roo.isGecko){
4522             body = "this.compiled = function(values){ return '" +
4523                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4524                     "';};";
4525         }else{
4526             body = ["this.compiled = function(values){ return ['"];
4527             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4528             body.push("'].join('');};");
4529             body = body.join('');
4530         }
4531         /**
4532          * eval:var:values
4533          * eval:var:fm
4534          */
4535         eval(body);
4536         return this;
4537     },
4538     
4539     // private function used to call members
4540     call : function(fnName, value, allValues){
4541         return this[fnName](value, allValues);
4542     },
4543     
4544     /**
4545      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4546      * @param {String/HTMLElement/Roo.Element} el The context element
4547      * @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'})
4548      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4549      * @return {HTMLElement/Roo.Element} The new node or Element
4550      */
4551     insertFirst: function(el, values, returnElement){
4552         return this.doInsert('afterBegin', el, values, returnElement);
4553     },
4554
4555     /**
4556      * Applies the supplied values to the template and inserts the new node(s) before el.
4557      * @param {String/HTMLElement/Roo.Element} el The context element
4558      * @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'})
4559      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4560      * @return {HTMLElement/Roo.Element} The new node or Element
4561      */
4562     insertBefore: function(el, values, returnElement){
4563         return this.doInsert('beforeBegin', el, values, returnElement);
4564     },
4565
4566     /**
4567      * Applies the supplied values to the template and inserts the new node(s) after el.
4568      * @param {String/HTMLElement/Roo.Element} el The context element
4569      * @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'})
4570      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4571      * @return {HTMLElement/Roo.Element} The new node or Element
4572      */
4573     insertAfter : function(el, values, returnElement){
4574         return this.doInsert('afterEnd', el, values, returnElement);
4575     },
4576     
4577     /**
4578      * Applies the supplied values to the template and appends the new node(s) to el.
4579      * @param {String/HTMLElement/Roo.Element} el The context element
4580      * @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'})
4581      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4582      * @return {HTMLElement/Roo.Element} The new node or Element
4583      */
4584     append : function(el, values, returnElement){
4585         return this.doInsert('beforeEnd', el, values, returnElement);
4586     },
4587
4588     doInsert : function(where, el, values, returnEl){
4589         el = Roo.getDom(el);
4590         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4591         return returnEl ? Roo.get(newNode, true) : newNode;
4592     },
4593
4594     /**
4595      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
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     overwrite : function(el, values, returnElement){
4602         el = Roo.getDom(el);
4603         el.innerHTML = this.applyTemplate(values);
4604         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4605     }
4606 };
4607 /**
4608  * Alias for {@link #applyTemplate}
4609  * @method
4610  */
4611 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4612
4613 // backwards compat
4614 Roo.DomHelper.Template = Roo.Template;
4615
4616 /**
4617  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4618  * @param {String/HTMLElement} el A DOM element or its id
4619  * @returns {Roo.Template} The created template
4620  * @static
4621  */
4622 Roo.Template.from = function(el){
4623     el = Roo.getDom(el);
4624     return new Roo.Template(el.value || el.innerHTML);
4625 };/*
4626  * Based on:
4627  * Ext JS Library 1.1.1
4628  * Copyright(c) 2006-2007, Ext JS, LLC.
4629  *
4630  * Originally Released Under LGPL - original licence link has changed is not relivant.
4631  *
4632  * Fork - LGPL
4633  * <script type="text/javascript">
4634  */
4635  
4636
4637 /*
4638  * This is code is also distributed under MIT license for use
4639  * with jQuery and prototype JavaScript libraries.
4640  */
4641 /**
4642  * @class Roo.DomQuery
4643 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).
4644 <p>
4645 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>
4646
4647 <p>
4648 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.
4649 </p>
4650 <h4>Element Selectors:</h4>
4651 <ul class="list">
4652     <li> <b>*</b> any element</li>
4653     <li> <b>E</b> an element with the tag E</li>
4654     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4655     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4656     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4657     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4658 </ul>
4659 <h4>Attribute Selectors:</h4>
4660 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4661 <ul class="list">
4662     <li> <b>E[foo]</b> has an attribute "foo"</li>
4663     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4664     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4665     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4666     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4667     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4668     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4669 </ul>
4670 <h4>Pseudo Classes:</h4>
4671 <ul class="list">
4672     <li> <b>E:first-child</b> E is the first child of its parent</li>
4673     <li> <b>E:last-child</b> E is the last child of its parent</li>
4674     <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>
4675     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4676     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4677     <li> <b>E:only-child</b> E is the only child of its parent</li>
4678     <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>
4679     <li> <b>E:first</b> the first E in the resultset</li>
4680     <li> <b>E:last</b> the last E in the resultset</li>
4681     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4682     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4683     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4684     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4685     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4686     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4687     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4688     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4689     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4690 </ul>
4691 <h4>CSS Value Selectors:</h4>
4692 <ul class="list">
4693     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4694     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4695     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4696     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4697     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4698     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4699 </ul>
4700  * @singleton
4701  */
4702 Roo.DomQuery = function(){
4703     var cache = {}, simpleCache = {}, valueCache = {};
4704     var nonSpace = /\S/;
4705     var trimRe = /^\s+|\s+$/g;
4706     var tplRe = /\{(\d+)\}/g;
4707     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4708     var tagTokenRe = /^(#)?([\w-\*]+)/;
4709     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4710
4711     function child(p, index){
4712         var i = 0;
4713         var n = p.firstChild;
4714         while(n){
4715             if(n.nodeType == 1){
4716                if(++i == index){
4717                    return n;
4718                }
4719             }
4720             n = n.nextSibling;
4721         }
4722         return null;
4723     };
4724
4725     function next(n){
4726         while((n = n.nextSibling) && n.nodeType != 1);
4727         return n;
4728     };
4729
4730     function prev(n){
4731         while((n = n.previousSibling) && n.nodeType != 1);
4732         return n;
4733     };
4734
4735     function children(d){
4736         var n = d.firstChild, ni = -1;
4737             while(n){
4738                 var nx = n.nextSibling;
4739                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4740                     d.removeChild(n);
4741                 }else{
4742                     n.nodeIndex = ++ni;
4743                 }
4744                 n = nx;
4745             }
4746             return this;
4747         };
4748
4749     function byClassName(c, a, v){
4750         if(!v){
4751             return c;
4752         }
4753         var r = [], ri = -1, cn;
4754         for(var i = 0, ci; ci = c[i]; i++){
4755             if((' '+ci.className+' ').indexOf(v) != -1){
4756                 r[++ri] = ci;
4757             }
4758         }
4759         return r;
4760     };
4761
4762     function attrValue(n, attr){
4763         if(!n.tagName && typeof n.length != "undefined"){
4764             n = n[0];
4765         }
4766         if(!n){
4767             return null;
4768         }
4769         if(attr == "for"){
4770             return n.htmlFor;
4771         }
4772         if(attr == "class" || attr == "className"){
4773             return n.className;
4774         }
4775         return n.getAttribute(attr) || n[attr];
4776
4777     };
4778
4779     function getNodes(ns, mode, tagName){
4780         var result = [], ri = -1, cs;
4781         if(!ns){
4782             return result;
4783         }
4784         tagName = tagName || "*";
4785         if(typeof ns.getElementsByTagName != "undefined"){
4786             ns = [ns];
4787         }
4788         if(!mode){
4789             for(var i = 0, ni; ni = ns[i]; i++){
4790                 cs = ni.getElementsByTagName(tagName);
4791                 for(var j = 0, ci; ci = cs[j]; j++){
4792                     result[++ri] = ci;
4793                 }
4794             }
4795         }else if(mode == "/" || mode == ">"){
4796             var utag = tagName.toUpperCase();
4797             for(var i = 0, ni, cn; ni = ns[i]; i++){
4798                 cn = ni.children || ni.childNodes;
4799                 for(var j = 0, cj; cj = cn[j]; j++){
4800                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4801                         result[++ri] = cj;
4802                     }
4803                 }
4804             }
4805         }else if(mode == "+"){
4806             var utag = tagName.toUpperCase();
4807             for(var i = 0, n; n = ns[i]; i++){
4808                 while((n = n.nextSibling) && n.nodeType != 1);
4809                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4810                     result[++ri] = n;
4811                 }
4812             }
4813         }else if(mode == "~"){
4814             for(var i = 0, n; n = ns[i]; i++){
4815                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4816                 if(n){
4817                     result[++ri] = n;
4818                 }
4819             }
4820         }
4821         return result;
4822     };
4823
4824     function concat(a, b){
4825         if(b.slice){
4826             return a.concat(b);
4827         }
4828         for(var i = 0, l = b.length; i < l; i++){
4829             a[a.length] = b[i];
4830         }
4831         return a;
4832     }
4833
4834     function byTag(cs, tagName){
4835         if(cs.tagName || cs == document){
4836             cs = [cs];
4837         }
4838         if(!tagName){
4839             return cs;
4840         }
4841         var r = [], ri = -1;
4842         tagName = tagName.toLowerCase();
4843         for(var i = 0, ci; ci = cs[i]; i++){
4844             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4845                 r[++ri] = ci;
4846             }
4847         }
4848         return r;
4849     };
4850
4851     function byId(cs, attr, id){
4852         if(cs.tagName || cs == document){
4853             cs = [cs];
4854         }
4855         if(!id){
4856             return cs;
4857         }
4858         var r = [], ri = -1;
4859         for(var i = 0,ci; ci = cs[i]; i++){
4860             if(ci && ci.id == id){
4861                 r[++ri] = ci;
4862                 return r;
4863             }
4864         }
4865         return r;
4866     };
4867
4868     function byAttribute(cs, attr, value, op, custom){
4869         var r = [], ri = -1, st = custom=="{";
4870         var f = Roo.DomQuery.operators[op];
4871         for(var i = 0, ci; ci = cs[i]; i++){
4872             var a;
4873             if(st){
4874                 a = Roo.DomQuery.getStyle(ci, attr);
4875             }
4876             else if(attr == "class" || attr == "className"){
4877                 a = ci.className;
4878             }else if(attr == "for"){
4879                 a = ci.htmlFor;
4880             }else if(attr == "href"){
4881                 a = ci.getAttribute("href", 2);
4882             }else{
4883                 a = ci.getAttribute(attr);
4884             }
4885             if((f && f(a, value)) || (!f && a)){
4886                 r[++ri] = ci;
4887             }
4888         }
4889         return r;
4890     };
4891
4892     function byPseudo(cs, name, value){
4893         return Roo.DomQuery.pseudos[name](cs, value);
4894     };
4895
4896     // This is for IE MSXML which does not support expandos.
4897     // IE runs the same speed using setAttribute, however FF slows way down
4898     // and Safari completely fails so they need to continue to use expandos.
4899     var isIE = window.ActiveXObject ? true : false;
4900
4901     // this eval is stop the compressor from
4902     // renaming the variable to something shorter
4903     
4904     /** eval:var:batch */
4905     var batch = 30803; 
4906
4907     var key = 30803;
4908
4909     function nodupIEXml(cs){
4910         var d = ++key;
4911         cs[0].setAttribute("_nodup", d);
4912         var r = [cs[0]];
4913         for(var i = 1, len = cs.length; i < len; i++){
4914             var c = cs[i];
4915             if(!c.getAttribute("_nodup") != d){
4916                 c.setAttribute("_nodup", d);
4917                 r[r.length] = c;
4918             }
4919         }
4920         for(var i = 0, len = cs.length; i < len; i++){
4921             cs[i].removeAttribute("_nodup");
4922         }
4923         return r;
4924     }
4925
4926     function nodup(cs){
4927         if(!cs){
4928             return [];
4929         }
4930         var len = cs.length, c, i, r = cs, cj, ri = -1;
4931         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4932             return cs;
4933         }
4934         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4935             return nodupIEXml(cs);
4936         }
4937         var d = ++key;
4938         cs[0]._nodup = d;
4939         for(i = 1; c = cs[i]; i++){
4940             if(c._nodup != d){
4941                 c._nodup = d;
4942             }else{
4943                 r = [];
4944                 for(var j = 0; j < i; j++){
4945                     r[++ri] = cs[j];
4946                 }
4947                 for(j = i+1; cj = cs[j]; j++){
4948                     if(cj._nodup != d){
4949                         cj._nodup = d;
4950                         r[++ri] = cj;
4951                     }
4952                 }
4953                 return r;
4954             }
4955         }
4956         return r;
4957     }
4958
4959     function quickDiffIEXml(c1, c2){
4960         var d = ++key;
4961         for(var i = 0, len = c1.length; i < len; i++){
4962             c1[i].setAttribute("_qdiff", d);
4963         }
4964         var r = [];
4965         for(var i = 0, len = c2.length; i < len; i++){
4966             if(c2[i].getAttribute("_qdiff") != d){
4967                 r[r.length] = c2[i];
4968             }
4969         }
4970         for(var i = 0, len = c1.length; i < len; i++){
4971            c1[i].removeAttribute("_qdiff");
4972         }
4973         return r;
4974     }
4975
4976     function quickDiff(c1, c2){
4977         var len1 = c1.length;
4978         if(!len1){
4979             return c2;
4980         }
4981         if(isIE && c1[0].selectSingleNode){
4982             return quickDiffIEXml(c1, c2);
4983         }
4984         var d = ++key;
4985         for(var i = 0; i < len1; i++){
4986             c1[i]._qdiff = d;
4987         }
4988         var r = [];
4989         for(var i = 0, len = c2.length; i < len; i++){
4990             if(c2[i]._qdiff != d){
4991                 r[r.length] = c2[i];
4992             }
4993         }
4994         return r;
4995     }
4996
4997     function quickId(ns, mode, root, id){
4998         if(ns == root){
4999            var d = root.ownerDocument || root;
5000            return d.getElementById(id);
5001         }
5002         ns = getNodes(ns, mode, "*");
5003         return byId(ns, null, id);
5004     }
5005
5006     return {
5007         getStyle : function(el, name){
5008             return Roo.fly(el).getStyle(name);
5009         },
5010         /**
5011          * Compiles a selector/xpath query into a reusable function. The returned function
5012          * takes one parameter "root" (optional), which is the context node from where the query should start.
5013          * @param {String} selector The selector/xpath query
5014          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5015          * @return {Function}
5016          */
5017         compile : function(path, type){
5018             type = type || "select";
5019             
5020             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5021             var q = path, mode, lq;
5022             var tk = Roo.DomQuery.matchers;
5023             var tklen = tk.length;
5024             var mm;
5025
5026             // accept leading mode switch
5027             var lmode = q.match(modeRe);
5028             if(lmode && lmode[1]){
5029                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5030                 q = q.replace(lmode[1], "");
5031             }
5032             // strip leading slashes
5033             while(path.substr(0, 1)=="/"){
5034                 path = path.substr(1);
5035             }
5036
5037             while(q && lq != q){
5038                 lq = q;
5039                 var tm = q.match(tagTokenRe);
5040                 if(type == "select"){
5041                     if(tm){
5042                         if(tm[1] == "#"){
5043                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5044                         }else{
5045                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5046                         }
5047                         q = q.replace(tm[0], "");
5048                     }else if(q.substr(0, 1) != '@'){
5049                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5050                     }
5051                 }else{
5052                     if(tm){
5053                         if(tm[1] == "#"){
5054                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5055                         }else{
5056                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5057                         }
5058                         q = q.replace(tm[0], "");
5059                     }
5060                 }
5061                 while(!(mm = q.match(modeRe))){
5062                     var matched = false;
5063                     for(var j = 0; j < tklen; j++){
5064                         var t = tk[j];
5065                         var m = q.match(t.re);
5066                         if(m){
5067                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5068                                                     return m[i];
5069                                                 });
5070                             q = q.replace(m[0], "");
5071                             matched = true;
5072                             break;
5073                         }
5074                     }
5075                     // prevent infinite loop on bad selector
5076                     if(!matched){
5077                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5078                     }
5079                 }
5080                 if(mm[1]){
5081                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5082                     q = q.replace(mm[1], "");
5083                 }
5084             }
5085             fn[fn.length] = "return nodup(n);\n}";
5086             
5087              /** 
5088               * list of variables that need from compression as they are used by eval.
5089              *  eval:var:batch 
5090              *  eval:var:nodup
5091              *  eval:var:byTag
5092              *  eval:var:ById
5093              *  eval:var:getNodes
5094              *  eval:var:quickId
5095              *  eval:var:mode
5096              *  eval:var:root
5097              *  eval:var:n
5098              *  eval:var:byClassName
5099              *  eval:var:byPseudo
5100              *  eval:var:byAttribute
5101              *  eval:var:attrValue
5102              * 
5103              **/ 
5104             eval(fn.join(""));
5105             return f;
5106         },
5107
5108         /**
5109          * Selects a group of elements.
5110          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5111          * @param {Node} root (optional) The start of the query (defaults to document).
5112          * @return {Array}
5113          */
5114         select : function(path, root, type){
5115             if(!root || root == document){
5116                 root = document;
5117             }
5118             if(typeof root == "string"){
5119                 root = document.getElementById(root);
5120             }
5121             var paths = path.split(",");
5122             var results = [];
5123             for(var i = 0, len = paths.length; i < len; i++){
5124                 var p = paths[i].replace(trimRe, "");
5125                 if(!cache[p]){
5126                     cache[p] = Roo.DomQuery.compile(p);
5127                     if(!cache[p]){
5128                         throw p + " is not a valid selector";
5129                     }
5130                 }
5131                 var result = cache[p](root);
5132                 if(result && result != document){
5133                     results = results.concat(result);
5134                 }
5135             }
5136             if(paths.length > 1){
5137                 return nodup(results);
5138             }
5139             return results;
5140         },
5141
5142         /**
5143          * Selects a single element.
5144          * @param {String} selector The selector/xpath query
5145          * @param {Node} root (optional) The start of the query (defaults to document).
5146          * @return {Element}
5147          */
5148         selectNode : function(path, root){
5149             return Roo.DomQuery.select(path, root)[0];
5150         },
5151
5152         /**
5153          * Selects the value of a node, optionally replacing null with the defaultValue.
5154          * @param {String} selector The selector/xpath query
5155          * @param {Node} root (optional) The start of the query (defaults to document).
5156          * @param {String} defaultValue
5157          */
5158         selectValue : function(path, root, defaultValue){
5159             path = path.replace(trimRe, "");
5160             if(!valueCache[path]){
5161                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5162             }
5163             var n = valueCache[path](root);
5164             n = n[0] ? n[0] : n;
5165             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5166             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5167         },
5168
5169         /**
5170          * Selects the value of a node, parsing integers and floats.
5171          * @param {String} selector The selector/xpath query
5172          * @param {Node} root (optional) The start of the query (defaults to document).
5173          * @param {Number} defaultValue
5174          * @return {Number}
5175          */
5176         selectNumber : function(path, root, defaultValue){
5177             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5178             return parseFloat(v);
5179         },
5180
5181         /**
5182          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5183          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5184          * @param {String} selector The simple selector to test
5185          * @return {Boolean}
5186          */
5187         is : function(el, ss){
5188             if(typeof el == "string"){
5189                 el = document.getElementById(el);
5190             }
5191             var isArray = (el instanceof Array);
5192             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5193             return isArray ? (result.length == el.length) : (result.length > 0);
5194         },
5195
5196         /**
5197          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5198          * @param {Array} el An array of elements to filter
5199          * @param {String} selector The simple selector to test
5200          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5201          * the selector instead of the ones that match
5202          * @return {Array}
5203          */
5204         filter : function(els, ss, nonMatches){
5205             ss = ss.replace(trimRe, "");
5206             if(!simpleCache[ss]){
5207                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5208             }
5209             var result = simpleCache[ss](els);
5210             return nonMatches ? quickDiff(result, els) : result;
5211         },
5212
5213         /**
5214          * Collection of matching regular expressions and code snippets.
5215          */
5216         matchers : [{
5217                 re: /^\.([\w-]+)/,
5218                 select: 'n = byClassName(n, null, " {1} ");'
5219             }, {
5220                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5221                 select: 'n = byPseudo(n, "{1}", "{2}");'
5222             },{
5223                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5224                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5225             }, {
5226                 re: /^#([\w-]+)/,
5227                 select: 'n = byId(n, null, "{1}");'
5228             },{
5229                 re: /^@([\w-]+)/,
5230                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5231             }
5232         ],
5233
5234         /**
5235          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5236          * 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;.
5237          */
5238         operators : {
5239             "=" : function(a, v){
5240                 return a == v;
5241             },
5242             "!=" : function(a, v){
5243                 return a != v;
5244             },
5245             "^=" : function(a, v){
5246                 return a && a.substr(0, v.length) == v;
5247             },
5248             "$=" : function(a, v){
5249                 return a && a.substr(a.length-v.length) == v;
5250             },
5251             "*=" : function(a, v){
5252                 return a && a.indexOf(v) !== -1;
5253             },
5254             "%=" : function(a, v){
5255                 return (a % v) == 0;
5256             },
5257             "|=" : function(a, v){
5258                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5259             },
5260             "~=" : function(a, v){
5261                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5262             }
5263         },
5264
5265         /**
5266          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5267          * and the argument (if any) supplied in the selector.
5268          */
5269         pseudos : {
5270             "first-child" : function(c){
5271                 var r = [], ri = -1, n;
5272                 for(var i = 0, ci; ci = n = c[i]; i++){
5273                     while((n = n.previousSibling) && n.nodeType != 1);
5274                     if(!n){
5275                         r[++ri] = ci;
5276                     }
5277                 }
5278                 return r;
5279             },
5280
5281             "last-child" : function(c){
5282                 var r = [], ri = -1, n;
5283                 for(var i = 0, ci; ci = n = c[i]; i++){
5284                     while((n = n.nextSibling) && n.nodeType != 1);
5285                     if(!n){
5286                         r[++ri] = ci;
5287                     }
5288                 }
5289                 return r;
5290             },
5291
5292             "nth-child" : function(c, a) {
5293                 var r = [], ri = -1;
5294                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5295                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5296                 for(var i = 0, n; n = c[i]; i++){
5297                     var pn = n.parentNode;
5298                     if (batch != pn._batch) {
5299                         var j = 0;
5300                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5301                             if(cn.nodeType == 1){
5302                                cn.nodeIndex = ++j;
5303                             }
5304                         }
5305                         pn._batch = batch;
5306                     }
5307                     if (f == 1) {
5308                         if (l == 0 || n.nodeIndex == l){
5309                             r[++ri] = n;
5310                         }
5311                     } else if ((n.nodeIndex + l) % f == 0){
5312                         r[++ri] = n;
5313                     }
5314                 }
5315
5316                 return r;
5317             },
5318
5319             "only-child" : function(c){
5320                 var r = [], ri = -1;;
5321                 for(var i = 0, ci; ci = c[i]; i++){
5322                     if(!prev(ci) && !next(ci)){
5323                         r[++ri] = ci;
5324                     }
5325                 }
5326                 return r;
5327             },
5328
5329             "empty" : function(c){
5330                 var r = [], ri = -1;
5331                 for(var i = 0, ci; ci = c[i]; i++){
5332                     var cns = ci.childNodes, j = 0, cn, empty = true;
5333                     while(cn = cns[j]){
5334                         ++j;
5335                         if(cn.nodeType == 1 || cn.nodeType == 3){
5336                             empty = false;
5337                             break;
5338                         }
5339                     }
5340                     if(empty){
5341                         r[++ri] = ci;
5342                     }
5343                 }
5344                 return r;
5345             },
5346
5347             "contains" : function(c, v){
5348                 var r = [], ri = -1;
5349                 for(var i = 0, ci; ci = c[i]; i++){
5350                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5351                         r[++ri] = ci;
5352                     }
5353                 }
5354                 return r;
5355             },
5356
5357             "nodeValue" : function(c, v){
5358                 var r = [], ri = -1;
5359                 for(var i = 0, ci; ci = c[i]; i++){
5360                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5361                         r[++ri] = ci;
5362                     }
5363                 }
5364                 return r;
5365             },
5366
5367             "checked" : function(c){
5368                 var r = [], ri = -1;
5369                 for(var i = 0, ci; ci = c[i]; i++){
5370                     if(ci.checked == true){
5371                         r[++ri] = ci;
5372                     }
5373                 }
5374                 return r;
5375             },
5376
5377             "not" : function(c, ss){
5378                 return Roo.DomQuery.filter(c, ss, true);
5379             },
5380
5381             "odd" : function(c){
5382                 return this["nth-child"](c, "odd");
5383             },
5384
5385             "even" : function(c){
5386                 return this["nth-child"](c, "even");
5387             },
5388
5389             "nth" : function(c, a){
5390                 return c[a-1] || [];
5391             },
5392
5393             "first" : function(c){
5394                 return c[0] || [];
5395             },
5396
5397             "last" : function(c){
5398                 return c[c.length-1] || [];
5399             },
5400
5401             "has" : function(c, ss){
5402                 var s = Roo.DomQuery.select;
5403                 var r = [], ri = -1;
5404                 for(var i = 0, ci; ci = c[i]; i++){
5405                     if(s(ss, ci).length > 0){
5406                         r[++ri] = ci;
5407                     }
5408                 }
5409                 return r;
5410             },
5411
5412             "next" : function(c, ss){
5413                 var is = Roo.DomQuery.is;
5414                 var r = [], ri = -1;
5415                 for(var i = 0, ci; ci = c[i]; i++){
5416                     var n = next(ci);
5417                     if(n && is(n, ss)){
5418                         r[++ri] = ci;
5419                     }
5420                 }
5421                 return r;
5422             },
5423
5424             "prev" : function(c, ss){
5425                 var is = Roo.DomQuery.is;
5426                 var r = [], ri = -1;
5427                 for(var i = 0, ci; ci = c[i]; i++){
5428                     var n = prev(ci);
5429                     if(n && is(n, ss)){
5430                         r[++ri] = ci;
5431                     }
5432                 }
5433                 return r;
5434             }
5435         }
5436     };
5437 }();
5438
5439 /**
5440  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5441  * @param {String} path The selector/xpath query
5442  * @param {Node} root (optional) The start of the query (defaults to document).
5443  * @return {Array}
5444  * @member Roo
5445  * @method query
5446  */
5447 Roo.query = Roo.DomQuery.select;
5448 /*
5449  * Based on:
5450  * Ext JS Library 1.1.1
5451  * Copyright(c) 2006-2007, Ext JS, LLC.
5452  *
5453  * Originally Released Under LGPL - original licence link has changed is not relivant.
5454  *
5455  * Fork - LGPL
5456  * <script type="text/javascript">
5457  */
5458
5459 /**
5460  * @class Roo.util.Observable
5461  * Base class that provides a common interface for publishing events. Subclasses are expected to
5462  * to have a property "events" with all the events defined.<br>
5463  * For example:
5464  * <pre><code>
5465  Employee = function(name){
5466     this.name = name;
5467     this.addEvents({
5468         "fired" : true,
5469         "quit" : true
5470     });
5471  }
5472  Roo.extend(Employee, Roo.util.Observable);
5473 </code></pre>
5474  * @param {Object} config properties to use (incuding events / listeners)
5475  */
5476
5477 Roo.util.Observable = function(cfg){
5478     
5479     cfg = cfg|| {};
5480     this.addEvents(cfg.events || {});
5481     if (cfg.events) {
5482         delete cfg.events; // make sure
5483     }
5484      
5485     Roo.apply(this, cfg);
5486     
5487     if(this.listeners){
5488         this.on(this.listeners);
5489         delete this.listeners;
5490     }
5491 };
5492 Roo.util.Observable.prototype = {
5493     /** 
5494  * @cfg {Object} listeners  list of events and functions to call for this object, 
5495  * For example :
5496  * <pre><code>
5497     listeners :  { 
5498        'click' : function(e) {
5499            ..... 
5500         } ,
5501         .... 
5502     } 
5503   </code></pre>
5504  */
5505     
5506     
5507     /**
5508      * Fires the specified event with the passed parameters (minus the event name).
5509      * @param {String} eventName
5510      * @param {Object...} args Variable number of parameters are passed to handlers
5511      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5512      */
5513     fireEvent : function(){
5514         var ce = this.events[arguments[0].toLowerCase()];
5515         if(typeof ce == "object"){
5516             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5517         }else{
5518             return true;
5519         }
5520     },
5521
5522     // private
5523     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5524
5525     /**
5526      * Appends an event handler to this component
5527      * @param {String}   eventName The type of event to listen for
5528      * @param {Function} handler The method the event invokes
5529      * @param {Object}   scope (optional) The scope in which to execute the handler
5530      * function. The handler function's "this" context.
5531      * @param {Object}   options (optional) An object containing handler configuration
5532      * properties. This may contain any of the following properties:<ul>
5533      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5534      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5535      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5536      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5537      * by the specified number of milliseconds. If the event fires again within that time, the original
5538      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5539      * </ul><br>
5540      * <p>
5541      * <b>Combining Options</b><br>
5542      * Using the options argument, it is possible to combine different types of listeners:<br>
5543      * <br>
5544      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5545                 <pre><code>
5546                 el.on('click', this.onClick, this, {
5547                         single: true,
5548                 delay: 100,
5549                 forumId: 4
5550                 });
5551                 </code></pre>
5552      * <p>
5553      * <b>Attaching multiple handlers in 1 call</b><br>
5554      * The method also allows for a single argument to be passed which is a config object containing properties
5555      * which specify multiple handlers.
5556      * <pre><code>
5557                 el.on({
5558                         'click': {
5559                         fn: this.onClick,
5560                         scope: this,
5561                         delay: 100
5562                 }, 
5563                 'mouseover': {
5564                         fn: this.onMouseOver,
5565                         scope: this
5566                 },
5567                 'mouseout': {
5568                         fn: this.onMouseOut,
5569                         scope: this
5570                 }
5571                 });
5572                 </code></pre>
5573      * <p>
5574      * Or a shorthand syntax which passes the same scope object to all handlers:
5575         <pre><code>
5576                 el.on({
5577                         'click': this.onClick,
5578                 'mouseover': this.onMouseOver,
5579                 'mouseout': this.onMouseOut,
5580                 scope: this
5581                 });
5582                 </code></pre>
5583      */
5584     addListener : function(eventName, fn, scope, o){
5585         if(typeof eventName == "object"){
5586             o = eventName;
5587             for(var e in o){
5588                 if(this.filterOptRe.test(e)){
5589                     continue;
5590                 }
5591                 if(typeof o[e] == "function"){
5592                     // shared options
5593                     this.addListener(e, o[e], o.scope,  o);
5594                 }else{
5595                     // individual options
5596                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5597                 }
5598             }
5599             return;
5600         }
5601         o = (!o || typeof o == "boolean") ? {} : o;
5602         eventName = eventName.toLowerCase();
5603         var ce = this.events[eventName] || true;
5604         if(typeof ce == "boolean"){
5605             ce = new Roo.util.Event(this, eventName);
5606             this.events[eventName] = ce;
5607         }
5608         ce.addListener(fn, scope, o);
5609     },
5610
5611     /**
5612      * Removes a listener
5613      * @param {String}   eventName     The type of event to listen for
5614      * @param {Function} handler        The handler to remove
5615      * @param {Object}   scope  (optional) The scope (this object) for the handler
5616      */
5617     removeListener : function(eventName, fn, scope){
5618         var ce = this.events[eventName.toLowerCase()];
5619         if(typeof ce == "object"){
5620             ce.removeListener(fn, scope);
5621         }
5622     },
5623
5624     /**
5625      * Removes all listeners for this object
5626      */
5627     purgeListeners : function(){
5628         for(var evt in this.events){
5629             if(typeof this.events[evt] == "object"){
5630                  this.events[evt].clearListeners();
5631             }
5632         }
5633     },
5634
5635     relayEvents : function(o, events){
5636         var createHandler = function(ename){
5637             return function(){
5638                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5639             };
5640         };
5641         for(var i = 0, len = events.length; i < len; i++){
5642             var ename = events[i];
5643             if(!this.events[ename]){ this.events[ename] = true; };
5644             o.on(ename, createHandler(ename), this);
5645         }
5646     },
5647
5648     /**
5649      * Used to define events on this Observable
5650      * @param {Object} object The object with the events defined
5651      */
5652     addEvents : function(o){
5653         if(!this.events){
5654             this.events = {};
5655         }
5656         Roo.applyIf(this.events, o);
5657     },
5658
5659     /**
5660      * Checks to see if this object has any listeners for a specified event
5661      * @param {String} eventName The name of the event to check for
5662      * @return {Boolean} True if the event is being listened for, else false
5663      */
5664     hasListener : function(eventName){
5665         var e = this.events[eventName];
5666         return typeof e == "object" && e.listeners.length > 0;
5667     }
5668 };
5669 /**
5670  * Appends an event handler to this element (shorthand for addListener)
5671  * @param {String}   eventName     The type of event to listen for
5672  * @param {Function} handler        The method the event invokes
5673  * @param {Object}   scope (optional) The scope in which to execute the handler
5674  * function. The handler function's "this" context.
5675  * @param {Object}   options  (optional)
5676  * @method
5677  */
5678 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5679 /**
5680  * Removes a listener (shorthand for removeListener)
5681  * @param {String}   eventName     The type of event to listen for
5682  * @param {Function} handler        The handler to remove
5683  * @param {Object}   scope  (optional) The scope (this object) for the handler
5684  * @method
5685  */
5686 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5687
5688 /**
5689  * Starts capture on the specified Observable. All events will be passed
5690  * to the supplied function with the event name + standard signature of the event
5691  * <b>before</b> the event is fired. If the supplied function returns false,
5692  * the event will not fire.
5693  * @param {Observable} o The Observable to capture
5694  * @param {Function} fn The function to call
5695  * @param {Object} scope (optional) The scope (this object) for the fn
5696  * @static
5697  */
5698 Roo.util.Observable.capture = function(o, fn, scope){
5699     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5700 };
5701
5702 /**
5703  * Removes <b>all</b> added captures from the Observable.
5704  * @param {Observable} o The Observable to release
5705  * @static
5706  */
5707 Roo.util.Observable.releaseCapture = function(o){
5708     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5709 };
5710
5711 (function(){
5712
5713     var createBuffered = function(h, o, scope){
5714         var task = new Roo.util.DelayedTask();
5715         return function(){
5716             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5717         };
5718     };
5719
5720     var createSingle = function(h, e, fn, scope){
5721         return function(){
5722             e.removeListener(fn, scope);
5723             return h.apply(scope, arguments);
5724         };
5725     };
5726
5727     var createDelayed = function(h, o, scope){
5728         return function(){
5729             var args = Array.prototype.slice.call(arguments, 0);
5730             setTimeout(function(){
5731                 h.apply(scope, args);
5732             }, o.delay || 10);
5733         };
5734     };
5735
5736     Roo.util.Event = function(obj, name){
5737         this.name = name;
5738         this.obj = obj;
5739         this.listeners = [];
5740     };
5741
5742     Roo.util.Event.prototype = {
5743         addListener : function(fn, scope, options){
5744             var o = options || {};
5745             scope = scope || this.obj;
5746             if(!this.isListening(fn, scope)){
5747                 var l = {fn: fn, scope: scope, options: o};
5748                 var h = fn;
5749                 if(o.delay){
5750                     h = createDelayed(h, o, scope);
5751                 }
5752                 if(o.single){
5753                     h = createSingle(h, this, fn, scope);
5754                 }
5755                 if(o.buffer){
5756                     h = createBuffered(h, o, scope);
5757                 }
5758                 l.fireFn = h;
5759                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5760                     this.listeners.push(l);
5761                 }else{
5762                     this.listeners = this.listeners.slice(0);
5763                     this.listeners.push(l);
5764                 }
5765             }
5766         },
5767
5768         findListener : function(fn, scope){
5769             scope = scope || this.obj;
5770             var ls = this.listeners;
5771             for(var i = 0, len = ls.length; i < len; i++){
5772                 var l = ls[i];
5773                 if(l.fn == fn && l.scope == scope){
5774                     return i;
5775                 }
5776             }
5777             return -1;
5778         },
5779
5780         isListening : function(fn, scope){
5781             return this.findListener(fn, scope) != -1;
5782         },
5783
5784         removeListener : function(fn, scope){
5785             var index;
5786             if((index = this.findListener(fn, scope)) != -1){
5787                 if(!this.firing){
5788                     this.listeners.splice(index, 1);
5789                 }else{
5790                     this.listeners = this.listeners.slice(0);
5791                     this.listeners.splice(index, 1);
5792                 }
5793                 return true;
5794             }
5795             return false;
5796         },
5797
5798         clearListeners : function(){
5799             this.listeners = [];
5800         },
5801
5802         fire : function(){
5803             var ls = this.listeners, scope, len = ls.length;
5804             if(len > 0){
5805                 this.firing = true;
5806                 var args = Array.prototype.slice.call(arguments, 0);
5807                 for(var i = 0; i < len; i++){
5808                     var l = ls[i];
5809                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5810                         this.firing = false;
5811                         return false;
5812                     }
5813                 }
5814                 this.firing = false;
5815             }
5816             return true;
5817         }
5818     };
5819 })();/*
5820  * Based on:
5821  * Ext JS Library 1.1.1
5822  * Copyright(c) 2006-2007, Ext JS, LLC.
5823  *
5824  * Originally Released Under LGPL - original licence link has changed is not relivant.
5825  *
5826  * Fork - LGPL
5827  * <script type="text/javascript">
5828  */
5829
5830 /**
5831  * @class Roo.EventManager
5832  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5833  * several useful events directly.
5834  * See {@link Roo.EventObject} for more details on normalized event objects.
5835  * @singleton
5836  */
5837 Roo.EventManager = function(){
5838     var docReadyEvent, docReadyProcId, docReadyState = false;
5839     var resizeEvent, resizeTask, textEvent, textSize;
5840     var E = Roo.lib.Event;
5841     var D = Roo.lib.Dom;
5842
5843
5844     var fireDocReady = function(){
5845         if(!docReadyState){
5846             docReadyState = true;
5847             Roo.isReady = true;
5848             if(docReadyProcId){
5849                 clearInterval(docReadyProcId);
5850             }
5851             if(Roo.isGecko || Roo.isOpera) {
5852                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5853             }
5854             if(Roo.isIE){
5855                 var defer = document.getElementById("ie-deferred-loader");
5856                 if(defer){
5857                     defer.onreadystatechange = null;
5858                     defer.parentNode.removeChild(defer);
5859                 }
5860             }
5861             if(docReadyEvent){
5862                 docReadyEvent.fire();
5863                 docReadyEvent.clearListeners();
5864             }
5865         }
5866     };
5867     
5868     var initDocReady = function(){
5869         docReadyEvent = new Roo.util.Event();
5870         if(Roo.isGecko || Roo.isOpera) {
5871             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5872         }else if(Roo.isIE){
5873             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5874             var defer = document.getElementById("ie-deferred-loader");
5875             defer.onreadystatechange = function(){
5876                 if(this.readyState == "complete"){
5877                     fireDocReady();
5878                 }
5879             };
5880         }else if(Roo.isSafari){ 
5881             docReadyProcId = setInterval(function(){
5882                 var rs = document.readyState;
5883                 if(rs == "complete") {
5884                     fireDocReady();     
5885                  }
5886             }, 10);
5887         }
5888         // no matter what, make sure it fires on load
5889         E.on(window, "load", fireDocReady);
5890     };
5891
5892     var createBuffered = function(h, o){
5893         var task = new Roo.util.DelayedTask(h);
5894         return function(e){
5895             // create new event object impl so new events don't wipe out properties
5896             e = new Roo.EventObjectImpl(e);
5897             task.delay(o.buffer, h, null, [e]);
5898         };
5899     };
5900
5901     var createSingle = function(h, el, ename, fn){
5902         return function(e){
5903             Roo.EventManager.removeListener(el, ename, fn);
5904             h(e);
5905         };
5906     };
5907
5908     var createDelayed = function(h, o){
5909         return function(e){
5910             // create new event object impl so new events don't wipe out properties
5911             e = new Roo.EventObjectImpl(e);
5912             setTimeout(function(){
5913                 h(e);
5914             }, o.delay || 10);
5915         };
5916     };
5917
5918     var listen = function(element, ename, opt, fn, scope){
5919         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5920         fn = fn || o.fn; scope = scope || o.scope;
5921         var el = Roo.getDom(element);
5922         if(!el){
5923             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5924         }
5925         var h = function(e){
5926             e = Roo.EventObject.setEvent(e);
5927             var t;
5928             if(o.delegate){
5929                 t = e.getTarget(o.delegate, el);
5930                 if(!t){
5931                     return;
5932                 }
5933             }else{
5934                 t = e.target;
5935             }
5936             if(o.stopEvent === true){
5937                 e.stopEvent();
5938             }
5939             if(o.preventDefault === true){
5940                e.preventDefault();
5941             }
5942             if(o.stopPropagation === true){
5943                 e.stopPropagation();
5944             }
5945
5946             if(o.normalized === false){
5947                 e = e.browserEvent;
5948             }
5949
5950             fn.call(scope || el, e, t, o);
5951         };
5952         if(o.delay){
5953             h = createDelayed(h, o);
5954         }
5955         if(o.single){
5956             h = createSingle(h, el, ename, fn);
5957         }
5958         if(o.buffer){
5959             h = createBuffered(h, o);
5960         }
5961         fn._handlers = fn._handlers || [];
5962         fn._handlers.push([Roo.id(el), ename, h]);
5963
5964         E.on(el, ename, h);
5965         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5966             el.addEventListener("DOMMouseScroll", h, false);
5967             E.on(window, 'unload', function(){
5968                 el.removeEventListener("DOMMouseScroll", h, false);
5969             });
5970         }
5971         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5972             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5973         }
5974         return h;
5975     };
5976
5977     var stopListening = function(el, ename, fn){
5978         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5979         if(hds){
5980             for(var i = 0, len = hds.length; i < len; i++){
5981                 var h = hds[i];
5982                 if(h[0] == id && h[1] == ename){
5983                     hd = h[2];
5984                     hds.splice(i, 1);
5985                     break;
5986                 }
5987             }
5988         }
5989         E.un(el, ename, hd);
5990         el = Roo.getDom(el);
5991         if(ename == "mousewheel" && el.addEventListener){
5992             el.removeEventListener("DOMMouseScroll", hd, false);
5993         }
5994         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5995             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5996         }
5997     };
5998
5999     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6000     
6001     var pub = {
6002         
6003         
6004         /** 
6005          * Fix for doc tools
6006          * @scope Roo.EventManager
6007          */
6008         
6009         
6010         /** 
6011          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6012          * object with a Roo.EventObject
6013          * @param {Function} fn        The method the event invokes
6014          * @param {Object}   scope    An object that becomes the scope of the handler
6015          * @param {boolean}  override If true, the obj passed in becomes
6016          *                             the execution scope of the listener
6017          * @return {Function} The wrapped function
6018          * @deprecated
6019          */
6020         wrap : function(fn, scope, override){
6021             return function(e){
6022                 Roo.EventObject.setEvent(e);
6023                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6024             };
6025         },
6026         
6027         /**
6028      * Appends an event handler to an element (shorthand for addListener)
6029      * @param {String/HTMLElement}   element        The html element or id to assign the
6030      * @param {String}   eventName The type of event to listen for
6031      * @param {Function} handler The method the event invokes
6032      * @param {Object}   scope (optional) The scope in which to execute the handler
6033      * function. The handler function's "this" context.
6034      * @param {Object}   options (optional) An object containing handler configuration
6035      * properties. This may contain any of the following properties:<ul>
6036      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6037      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6038      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6039      * <li>preventDefault {Boolean} True to prevent the default action</li>
6040      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6041      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6042      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6043      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6044      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6045      * by the specified number of milliseconds. If the event fires again within that time, the original
6046      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6047      * </ul><br>
6048      * <p>
6049      * <b>Combining Options</b><br>
6050      * Using the options argument, it is possible to combine different types of listeners:<br>
6051      * <br>
6052      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6053      * Code:<pre><code>
6054 el.on('click', this.onClick, this, {
6055     single: true,
6056     delay: 100,
6057     stopEvent : true,
6058     forumId: 4
6059 });</code></pre>
6060      * <p>
6061      * <b>Attaching multiple handlers in 1 call</b><br>
6062       * The method also allows for a single argument to be passed which is a config object containing properties
6063      * which specify multiple handlers.
6064      * <p>
6065      * Code:<pre><code>
6066 el.on({
6067     'click' : {
6068         fn: this.onClick
6069         scope: this,
6070         delay: 100
6071     },
6072     'mouseover' : {
6073         fn: this.onMouseOver
6074         scope: this
6075     },
6076     'mouseout' : {
6077         fn: this.onMouseOut
6078         scope: this
6079     }
6080 });</code></pre>
6081      * <p>
6082      * Or a shorthand syntax:<br>
6083      * Code:<pre><code>
6084 el.on({
6085     'click' : this.onClick,
6086     'mouseover' : this.onMouseOver,
6087     'mouseout' : this.onMouseOut
6088     scope: this
6089 });</code></pre>
6090      */
6091         addListener : function(element, eventName, fn, scope, options){
6092             if(typeof eventName == "object"){
6093                 var o = eventName;
6094                 for(var e in o){
6095                     if(propRe.test(e)){
6096                         continue;
6097                     }
6098                     if(typeof o[e] == "function"){
6099                         // shared options
6100                         listen(element, e, o, o[e], o.scope);
6101                     }else{
6102                         // individual options
6103                         listen(element, e, o[e]);
6104                     }
6105                 }
6106                 return;
6107             }
6108             return listen(element, eventName, options, fn, scope);
6109         },
6110         
6111         /**
6112          * Removes an event handler
6113          *
6114          * @param {String/HTMLElement}   element        The id or html element to remove the 
6115          *                             event from
6116          * @param {String}   eventName     The type of event
6117          * @param {Function} fn
6118          * @return {Boolean} True if a listener was actually removed
6119          */
6120         removeListener : function(element, eventName, fn){
6121             return stopListening(element, eventName, fn);
6122         },
6123         
6124         /**
6125          * Fires when the document is ready (before onload and before images are loaded). Can be 
6126          * accessed shorthanded Roo.onReady().
6127          * @param {Function} fn        The method the event invokes
6128          * @param {Object}   scope    An  object that becomes the scope of the handler
6129          * @param {boolean}  options
6130          */
6131         onDocumentReady : function(fn, scope, options){
6132             if(docReadyState){ // if it already fired
6133                 docReadyEvent.addListener(fn, scope, options);
6134                 docReadyEvent.fire();
6135                 docReadyEvent.clearListeners();
6136                 return;
6137             }
6138             if(!docReadyEvent){
6139                 initDocReady();
6140             }
6141             docReadyEvent.addListener(fn, scope, options);
6142         },
6143         
6144         /**
6145          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6146          * @param {Function} fn        The method the event invokes
6147          * @param {Object}   scope    An object that becomes the scope of the handler
6148          * @param {boolean}  options
6149          */
6150         onWindowResize : function(fn, scope, options){
6151             if(!resizeEvent){
6152                 resizeEvent = new Roo.util.Event();
6153                 resizeTask = new Roo.util.DelayedTask(function(){
6154                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6155                 });
6156                 E.on(window, "resize", function(){
6157                     if(Roo.isIE){
6158                         resizeTask.delay(50);
6159                     }else{
6160                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6161                     }
6162                 });
6163             }
6164             resizeEvent.addListener(fn, scope, options);
6165         },
6166
6167         /**
6168          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6169          * @param {Function} fn        The method the event invokes
6170          * @param {Object}   scope    An object that becomes the scope of the handler
6171          * @param {boolean}  options
6172          */
6173         onTextResize : function(fn, scope, options){
6174             if(!textEvent){
6175                 textEvent = new Roo.util.Event();
6176                 var textEl = new Roo.Element(document.createElement('div'));
6177                 textEl.dom.className = 'x-text-resize';
6178                 textEl.dom.innerHTML = 'X';
6179                 textEl.appendTo(document.body);
6180                 textSize = textEl.dom.offsetHeight;
6181                 setInterval(function(){
6182                     if(textEl.dom.offsetHeight != textSize){
6183                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6184                     }
6185                 }, this.textResizeInterval);
6186             }
6187             textEvent.addListener(fn, scope, options);
6188         },
6189
6190         /**
6191          * Removes the passed window resize listener.
6192          * @param {Function} fn        The method the event invokes
6193          * @param {Object}   scope    The scope of handler
6194          */
6195         removeResizeListener : function(fn, scope){
6196             if(resizeEvent){
6197                 resizeEvent.removeListener(fn, scope);
6198             }
6199         },
6200
6201         // private
6202         fireResize : function(){
6203             if(resizeEvent){
6204                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6205             }   
6206         },
6207         /**
6208          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6209          */
6210         ieDeferSrc : false,
6211         /**
6212          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6213          */
6214         textResizeInterval : 50
6215     };
6216     
6217     /**
6218      * Fix for doc tools
6219      * @scopeAlias pub=Roo.EventManager
6220      */
6221     
6222      /**
6223      * Appends an event handler to an element (shorthand for addListener)
6224      * @param {String/HTMLElement}   element        The html element or id to assign the
6225      * @param {String}   eventName The type of event to listen for
6226      * @param {Function} handler The method the event invokes
6227      * @param {Object}   scope (optional) The scope in which to execute the handler
6228      * function. The handler function's "this" context.
6229      * @param {Object}   options (optional) An object containing handler configuration
6230      * properties. This may contain any of the following properties:<ul>
6231      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6232      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6233      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6234      * <li>preventDefault {Boolean} True to prevent the default action</li>
6235      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6236      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6237      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6238      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6239      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6240      * by the specified number of milliseconds. If the event fires again within that time, the original
6241      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6242      * </ul><br>
6243      * <p>
6244      * <b>Combining Options</b><br>
6245      * Using the options argument, it is possible to combine different types of listeners:<br>
6246      * <br>
6247      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6248      * Code:<pre><code>
6249 el.on('click', this.onClick, this, {
6250     single: true,
6251     delay: 100,
6252     stopEvent : true,
6253     forumId: 4
6254 });</code></pre>
6255      * <p>
6256      * <b>Attaching multiple handlers in 1 call</b><br>
6257       * The method also allows for a single argument to be passed which is a config object containing properties
6258      * which specify multiple handlers.
6259      * <p>
6260      * Code:<pre><code>
6261 el.on({
6262     'click' : {
6263         fn: this.onClick
6264         scope: this,
6265         delay: 100
6266     },
6267     'mouseover' : {
6268         fn: this.onMouseOver
6269         scope: this
6270     },
6271     'mouseout' : {
6272         fn: this.onMouseOut
6273         scope: this
6274     }
6275 });</code></pre>
6276      * <p>
6277      * Or a shorthand syntax:<br>
6278      * Code:<pre><code>
6279 el.on({
6280     'click' : this.onClick,
6281     'mouseover' : this.onMouseOver,
6282     'mouseout' : this.onMouseOut
6283     scope: this
6284 });</code></pre>
6285      */
6286     pub.on = pub.addListener;
6287     pub.un = pub.removeListener;
6288
6289     pub.stoppedMouseDownEvent = new Roo.util.Event();
6290     return pub;
6291 }();
6292 /**
6293   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6294   * @param {Function} fn        The method the event invokes
6295   * @param {Object}   scope    An  object that becomes the scope of the handler
6296   * @param {boolean}  override If true, the obj passed in becomes
6297   *                             the execution scope of the listener
6298   * @member Roo
6299   * @method onReady
6300  */
6301 Roo.onReady = Roo.EventManager.onDocumentReady;
6302
6303 Roo.onReady(function(){
6304     var bd = Roo.get(document.body);
6305     if(!bd){ return; }
6306
6307     var cls = [
6308             Roo.isIE ? "roo-ie"
6309             : Roo.isGecko ? "roo-gecko"
6310             : Roo.isOpera ? "roo-opera"
6311             : Roo.isSafari ? "roo-safari" : ""];
6312
6313     if(Roo.isMac){
6314         cls.push("roo-mac");
6315     }
6316     if(Roo.isLinux){
6317         cls.push("roo-linux");
6318     }
6319     if(Roo.isBorderBox){
6320         cls.push('roo-border-box');
6321     }
6322     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6323         var p = bd.dom.parentNode;
6324         if(p){
6325             p.className += ' roo-strict';
6326         }
6327     }
6328     bd.addClass(cls.join(' '));
6329 });
6330
6331 /**
6332  * @class Roo.EventObject
6333  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6334  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6335  * Example:
6336  * <pre><code>
6337  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6338     e.preventDefault();
6339     var target = e.getTarget();
6340     ...
6341  }
6342  var myDiv = Roo.get("myDiv");
6343  myDiv.on("click", handleClick);
6344  //or
6345  Roo.EventManager.on("myDiv", 'click', handleClick);
6346  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6347  </code></pre>
6348  * @singleton
6349  */
6350 Roo.EventObject = function(){
6351     
6352     var E = Roo.lib.Event;
6353     
6354     // safari keypress events for special keys return bad keycodes
6355     var safariKeys = {
6356         63234 : 37, // left
6357         63235 : 39, // right
6358         63232 : 38, // up
6359         63233 : 40, // down
6360         63276 : 33, // page up
6361         63277 : 34, // page down
6362         63272 : 46, // delete
6363         63273 : 36, // home
6364         63275 : 35  // end
6365     };
6366
6367     // normalize button clicks
6368     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6369                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6370
6371     Roo.EventObjectImpl = function(e){
6372         if(e){
6373             this.setEvent(e.browserEvent || e);
6374         }
6375     };
6376     Roo.EventObjectImpl.prototype = {
6377         /**
6378          * Used to fix doc tools.
6379          * @scope Roo.EventObject.prototype
6380          */
6381             
6382
6383         
6384         
6385         /** The normal browser event */
6386         browserEvent : null,
6387         /** The button pressed in a mouse event */
6388         button : -1,
6389         /** True if the shift key was down during the event */
6390         shiftKey : false,
6391         /** True if the control key was down during the event */
6392         ctrlKey : false,
6393         /** True if the alt key was down during the event */
6394         altKey : false,
6395
6396         /** Key constant 
6397         * @type Number */
6398         BACKSPACE : 8,
6399         /** Key constant 
6400         * @type Number */
6401         TAB : 9,
6402         /** Key constant 
6403         * @type Number */
6404         RETURN : 13,
6405         /** Key constant 
6406         * @type Number */
6407         ENTER : 13,
6408         /** Key constant 
6409         * @type Number */
6410         SHIFT : 16,
6411         /** Key constant 
6412         * @type Number */
6413         CONTROL : 17,
6414         /** Key constant 
6415         * @type Number */
6416         ESC : 27,
6417         /** Key constant 
6418         * @type Number */
6419         SPACE : 32,
6420         /** Key constant 
6421         * @type Number */
6422         PAGEUP : 33,
6423         /** Key constant 
6424         * @type Number */
6425         PAGEDOWN : 34,
6426         /** Key constant 
6427         * @type Number */
6428         END : 35,
6429         /** Key constant 
6430         * @type Number */
6431         HOME : 36,
6432         /** Key constant 
6433         * @type Number */
6434         LEFT : 37,
6435         /** Key constant 
6436         * @type Number */
6437         UP : 38,
6438         /** Key constant 
6439         * @type Number */
6440         RIGHT : 39,
6441         /** Key constant 
6442         * @type Number */
6443         DOWN : 40,
6444         /** Key constant 
6445         * @type Number */
6446         DELETE : 46,
6447         /** Key constant 
6448         * @type Number */
6449         F5 : 116,
6450
6451            /** @private */
6452         setEvent : function(e){
6453             if(e == this || (e && e.browserEvent)){ // already wrapped
6454                 return e;
6455             }
6456             this.browserEvent = e;
6457             if(e){
6458                 // normalize buttons
6459                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6460                 if(e.type == 'click' && this.button == -1){
6461                     this.button = 0;
6462                 }
6463                 this.type = e.type;
6464                 this.shiftKey = e.shiftKey;
6465                 // mac metaKey behaves like ctrlKey
6466                 this.ctrlKey = e.ctrlKey || e.metaKey;
6467                 this.altKey = e.altKey;
6468                 // in getKey these will be normalized for the mac
6469                 this.keyCode = e.keyCode;
6470                 // keyup warnings on firefox.
6471                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6472                 // cache the target for the delayed and or buffered events
6473                 this.target = E.getTarget(e);
6474                 // same for XY
6475                 this.xy = E.getXY(e);
6476             }else{
6477                 this.button = -1;
6478                 this.shiftKey = false;
6479                 this.ctrlKey = false;
6480                 this.altKey = false;
6481                 this.keyCode = 0;
6482                 this.charCode =0;
6483                 this.target = null;
6484                 this.xy = [0, 0];
6485             }
6486             return this;
6487         },
6488
6489         /**
6490          * Stop the event (preventDefault and stopPropagation)
6491          */
6492         stopEvent : function(){
6493             if(this.browserEvent){
6494                 if(this.browserEvent.type == 'mousedown'){
6495                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6496                 }
6497                 E.stopEvent(this.browserEvent);
6498             }
6499         },
6500
6501         /**
6502          * Prevents the browsers default handling of the event.
6503          */
6504         preventDefault : function(){
6505             if(this.browserEvent){
6506                 E.preventDefault(this.browserEvent);
6507             }
6508         },
6509
6510         /** @private */
6511         isNavKeyPress : function(){
6512             var k = this.keyCode;
6513             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6514             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6515         },
6516
6517         isSpecialKey : function(){
6518             var k = this.keyCode;
6519             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6520             (k == 16) || (k == 17) ||
6521             (k >= 18 && k <= 20) ||
6522             (k >= 33 && k <= 35) ||
6523             (k >= 36 && k <= 39) ||
6524             (k >= 44 && k <= 45);
6525         },
6526         /**
6527          * Cancels bubbling of the event.
6528          */
6529         stopPropagation : function(){
6530             if(this.browserEvent){
6531                 if(this.type == 'mousedown'){
6532                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6533                 }
6534                 E.stopPropagation(this.browserEvent);
6535             }
6536         },
6537
6538         /**
6539          * Gets the key code for the event.
6540          * @return {Number}
6541          */
6542         getCharCode : function(){
6543             return this.charCode || this.keyCode;
6544         },
6545
6546         /**
6547          * Returns a normalized keyCode for the event.
6548          * @return {Number} The key code
6549          */
6550         getKey : function(){
6551             var k = this.keyCode || this.charCode;
6552             return Roo.isSafari ? (safariKeys[k] || k) : k;
6553         },
6554
6555         /**
6556          * Gets the x coordinate of the event.
6557          * @return {Number}
6558          */
6559         getPageX : function(){
6560             return this.xy[0];
6561         },
6562
6563         /**
6564          * Gets the y coordinate of the event.
6565          * @return {Number}
6566          */
6567         getPageY : function(){
6568             return this.xy[1];
6569         },
6570
6571         /**
6572          * Gets the time of the event.
6573          * @return {Number}
6574          */
6575         getTime : function(){
6576             if(this.browserEvent){
6577                 return E.getTime(this.browserEvent);
6578             }
6579             return null;
6580         },
6581
6582         /**
6583          * Gets the page coordinates of the event.
6584          * @return {Array} The xy values like [x, y]
6585          */
6586         getXY : function(){
6587             return this.xy;
6588         },
6589
6590         /**
6591          * Gets the target for the event.
6592          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6593          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6594                 search as a number or element (defaults to 10 || document.body)
6595          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6596          * @return {HTMLelement}
6597          */
6598         getTarget : function(selector, maxDepth, returnEl){
6599             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6600         },
6601         /**
6602          * Gets the related target.
6603          * @return {HTMLElement}
6604          */
6605         getRelatedTarget : function(){
6606             if(this.browserEvent){
6607                 return E.getRelatedTarget(this.browserEvent);
6608             }
6609             return null;
6610         },
6611
6612         /**
6613          * Normalizes mouse wheel delta across browsers
6614          * @return {Number} The delta
6615          */
6616         getWheelDelta : function(){
6617             var e = this.browserEvent;
6618             var delta = 0;
6619             if(e.wheelDelta){ /* IE/Opera. */
6620                 delta = e.wheelDelta/120;
6621             }else if(e.detail){ /* Mozilla case. */
6622                 delta = -e.detail/3;
6623             }
6624             return delta;
6625         },
6626
6627         /**
6628          * Returns true if the control, meta, shift or alt key was pressed during this event.
6629          * @return {Boolean}
6630          */
6631         hasModifier : function(){
6632             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6633         },
6634
6635         /**
6636          * Returns true if the target of this event equals el or is a child of el
6637          * @param {String/HTMLElement/Element} el
6638          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6639          * @return {Boolean}
6640          */
6641         within : function(el, related){
6642             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6643             return t && Roo.fly(el).contains(t);
6644         },
6645
6646         getPoint : function(){
6647             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6648         }
6649     };
6650
6651     return new Roo.EventObjectImpl();
6652 }();
6653             
6654     /*
6655  * Based on:
6656  * Ext JS Library 1.1.1
6657  * Copyright(c) 2006-2007, Ext JS, LLC.
6658  *
6659  * Originally Released Under LGPL - original licence link has changed is not relivant.
6660  *
6661  * Fork - LGPL
6662  * <script type="text/javascript">
6663  */
6664
6665  
6666 // was in Composite Element!??!?!
6667  
6668 (function(){
6669     var D = Roo.lib.Dom;
6670     var E = Roo.lib.Event;
6671     var A = Roo.lib.Anim;
6672
6673     // local style camelizing for speed
6674     var propCache = {};
6675     var camelRe = /(-[a-z])/gi;
6676     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6677     var view = document.defaultView;
6678
6679 /**
6680  * @class Roo.Element
6681  * Represents an Element in the DOM.<br><br>
6682  * Usage:<br>
6683 <pre><code>
6684 var el = Roo.get("my-div");
6685
6686 // or with getEl
6687 var el = getEl("my-div");
6688
6689 // or with a DOM element
6690 var el = Roo.get(myDivElement);
6691 </code></pre>
6692  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6693  * each call instead of constructing a new one.<br><br>
6694  * <b>Animations</b><br />
6695  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6696  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6697 <pre>
6698 Option    Default   Description
6699 --------- --------  ---------------------------------------------
6700 duration  .35       The duration of the animation in seconds
6701 easing    easeOut   The YUI easing method
6702 callback  none      A function to execute when the anim completes
6703 scope     this      The scope (this) of the callback function
6704 </pre>
6705 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6706 * manipulate the animation. Here's an example:
6707 <pre><code>
6708 var el = Roo.get("my-div");
6709
6710 // no animation
6711 el.setWidth(100);
6712
6713 // default animation
6714 el.setWidth(100, true);
6715
6716 // animation with some options set
6717 el.setWidth(100, {
6718     duration: 1,
6719     callback: this.foo,
6720     scope: this
6721 });
6722
6723 // using the "anim" property to get the Anim object
6724 var opt = {
6725     duration: 1,
6726     callback: this.foo,
6727     scope: this
6728 };
6729 el.setWidth(100, opt);
6730 ...
6731 if(opt.anim.isAnimated()){
6732     opt.anim.stop();
6733 }
6734 </code></pre>
6735 * <b> Composite (Collections of) Elements</b><br />
6736  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6737  * @constructor Create a new Element directly.
6738  * @param {String/HTMLElement} element
6739  * @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).
6740  */
6741     Roo.Element = function(element, forceNew){
6742         var dom = typeof element == "string" ?
6743                 document.getElementById(element) : element;
6744         if(!dom){ // invalid id/element
6745             return null;
6746         }
6747         var id = dom.id;
6748         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6749             return Roo.Element.cache[id];
6750         }
6751
6752         /**
6753          * The DOM element
6754          * @type HTMLElement
6755          */
6756         this.dom = dom;
6757
6758         /**
6759          * The DOM element ID
6760          * @type String
6761          */
6762         this.id = id || Roo.id(dom);
6763     };
6764
6765     var El = Roo.Element;
6766
6767     El.prototype = {
6768         /**
6769          * The element's default display mode  (defaults to "")
6770          * @type String
6771          */
6772         originalDisplay : "",
6773
6774         visibilityMode : 1,
6775         /**
6776          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6777          * @type String
6778          */
6779         defaultUnit : "px",
6780         /**
6781          * Sets the element's visibility mode. When setVisible() is called it
6782          * will use this to determine whether to set the visibility or the display property.
6783          * @param visMode Element.VISIBILITY or Element.DISPLAY
6784          * @return {Roo.Element} this
6785          */
6786         setVisibilityMode : function(visMode){
6787             this.visibilityMode = visMode;
6788             return this;
6789         },
6790         /**
6791          * Convenience method for setVisibilityMode(Element.DISPLAY)
6792          * @param {String} display (optional) What to set display to when visible
6793          * @return {Roo.Element} this
6794          */
6795         enableDisplayMode : function(display){
6796             this.setVisibilityMode(El.DISPLAY);
6797             if(typeof display != "undefined") this.originalDisplay = display;
6798             return this;
6799         },
6800
6801         /**
6802          * 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)
6803          * @param {String} selector The simple selector to test
6804          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6805                 search as a number or element (defaults to 10 || document.body)
6806          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6807          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6808          */
6809         findParent : function(simpleSelector, maxDepth, returnEl){
6810             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6811             maxDepth = maxDepth || 50;
6812             if(typeof maxDepth != "number"){
6813                 stopEl = Roo.getDom(maxDepth);
6814                 maxDepth = 10;
6815             }
6816             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6817                 if(dq.is(p, simpleSelector)){
6818                     return returnEl ? Roo.get(p) : p;
6819                 }
6820                 depth++;
6821                 p = p.parentNode;
6822             }
6823             return null;
6824         },
6825
6826
6827         /**
6828          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6829          * @param {String} selector The simple selector to test
6830          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6831                 search as a number or element (defaults to 10 || document.body)
6832          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6833          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6834          */
6835         findParentNode : function(simpleSelector, maxDepth, returnEl){
6836             var p = Roo.fly(this.dom.parentNode, '_internal');
6837             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6838         },
6839
6840         /**
6841          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6842          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6843          * @param {String} selector The simple selector to test
6844          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6845                 search as a number or element (defaults to 10 || document.body)
6846          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6847          */
6848         up : function(simpleSelector, maxDepth){
6849             return this.findParentNode(simpleSelector, maxDepth, true);
6850         },
6851
6852
6853
6854         /**
6855          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6856          * @param {String} selector The simple selector to test
6857          * @return {Boolean} True if this element matches the selector, else false
6858          */
6859         is : function(simpleSelector){
6860             return Roo.DomQuery.is(this.dom, simpleSelector);
6861         },
6862
6863         /**
6864          * Perform animation on this element.
6865          * @param {Object} args The YUI animation control args
6866          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6867          * @param {Function} onComplete (optional) Function to call when animation completes
6868          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6869          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6870          * @return {Roo.Element} this
6871          */
6872         animate : function(args, duration, onComplete, easing, animType){
6873             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6874             return this;
6875         },
6876
6877         /*
6878          * @private Internal animation call
6879          */
6880         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6881             animType = animType || 'run';
6882             opt = opt || {};
6883             var anim = Roo.lib.Anim[animType](
6884                 this.dom, args,
6885                 (opt.duration || defaultDur) || .35,
6886                 (opt.easing || defaultEase) || 'easeOut',
6887                 function(){
6888                     Roo.callback(cb, this);
6889                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6890                 },
6891                 this
6892             );
6893             opt.anim = anim;
6894             return anim;
6895         },
6896
6897         // private legacy anim prep
6898         preanim : function(a, i){
6899             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6900         },
6901
6902         /**
6903          * Removes worthless text nodes
6904          * @param {Boolean} forceReclean (optional) By default the element
6905          * keeps track if it has been cleaned already so
6906          * you can call this over and over. However, if you update the element and
6907          * need to force a reclean, you can pass true.
6908          */
6909         clean : function(forceReclean){
6910             if(this.isCleaned && forceReclean !== true){
6911                 return this;
6912             }
6913             var ns = /\S/;
6914             var d = this.dom, n = d.firstChild, ni = -1;
6915             while(n){
6916                 var nx = n.nextSibling;
6917                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6918                     d.removeChild(n);
6919                 }else{
6920                     n.nodeIndex = ++ni;
6921                 }
6922                 n = nx;
6923             }
6924             this.isCleaned = true;
6925             return this;
6926         },
6927
6928         // private
6929         calcOffsetsTo : function(el){
6930             el = Roo.get(el);
6931             var d = el.dom;
6932             var restorePos = false;
6933             if(el.getStyle('position') == 'static'){
6934                 el.position('relative');
6935                 restorePos = true;
6936             }
6937             var x = 0, y =0;
6938             var op = this.dom;
6939             while(op && op != d && op.tagName != 'HTML'){
6940                 x+= op.offsetLeft;
6941                 y+= op.offsetTop;
6942                 op = op.offsetParent;
6943             }
6944             if(restorePos){
6945                 el.position('static');
6946             }
6947             return [x, y];
6948         },
6949
6950         /**
6951          * Scrolls this element into view within the passed container.
6952          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6953          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6954          * @return {Roo.Element} this
6955          */
6956         scrollIntoView : function(container, hscroll){
6957             var c = Roo.getDom(container) || document.body;
6958             var el = this.dom;
6959
6960             var o = this.calcOffsetsTo(c),
6961                 l = o[0],
6962                 t = o[1],
6963                 b = t+el.offsetHeight,
6964                 r = l+el.offsetWidth;
6965
6966             var ch = c.clientHeight;
6967             var ct = parseInt(c.scrollTop, 10);
6968             var cl = parseInt(c.scrollLeft, 10);
6969             var cb = ct + ch;
6970             var cr = cl + c.clientWidth;
6971
6972             if(t < ct){
6973                 c.scrollTop = t;
6974             }else if(b > cb){
6975                 c.scrollTop = b-ch;
6976             }
6977
6978             if(hscroll !== false){
6979                 if(l < cl){
6980                     c.scrollLeft = l;
6981                 }else if(r > cr){
6982                     c.scrollLeft = r-c.clientWidth;
6983                 }
6984             }
6985             return this;
6986         },
6987
6988         // private
6989         scrollChildIntoView : function(child, hscroll){
6990             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6991         },
6992
6993         /**
6994          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6995          * the new height may not be available immediately.
6996          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6997          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6998          * @param {Function} onComplete (optional) Function to call when animation completes
6999          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7000          * @return {Roo.Element} this
7001          */
7002         autoHeight : function(animate, duration, onComplete, easing){
7003             var oldHeight = this.getHeight();
7004             this.clip();
7005             this.setHeight(1); // force clipping
7006             setTimeout(function(){
7007                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7008                 if(!animate){
7009                     this.setHeight(height);
7010                     this.unclip();
7011                     if(typeof onComplete == "function"){
7012                         onComplete();
7013                     }
7014                 }else{
7015                     this.setHeight(oldHeight); // restore original height
7016                     this.setHeight(height, animate, duration, function(){
7017                         this.unclip();
7018                         if(typeof onComplete == "function") onComplete();
7019                     }.createDelegate(this), easing);
7020                 }
7021             }.createDelegate(this), 0);
7022             return this;
7023         },
7024
7025         /**
7026          * Returns true if this element is an ancestor of the passed element
7027          * @param {HTMLElement/String} el The element to check
7028          * @return {Boolean} True if this element is an ancestor of el, else false
7029          */
7030         contains : function(el){
7031             if(!el){return false;}
7032             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7033         },
7034
7035         /**
7036          * Checks whether the element is currently visible using both visibility and display properties.
7037          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7038          * @return {Boolean} True if the element is currently visible, else false
7039          */
7040         isVisible : function(deep) {
7041             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7042             if(deep !== true || !vis){
7043                 return vis;
7044             }
7045             var p = this.dom.parentNode;
7046             while(p && p.tagName.toLowerCase() != "body"){
7047                 if(!Roo.fly(p, '_isVisible').isVisible()){
7048                     return false;
7049                 }
7050                 p = p.parentNode;
7051             }
7052             return true;
7053         },
7054
7055         /**
7056          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7057          * @param {String} selector The CSS selector
7058          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7059          * @return {CompositeElement/CompositeElementLite} The composite element
7060          */
7061         select : function(selector, unique){
7062             return El.select(selector, unique, this.dom);
7063         },
7064
7065         /**
7066          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7067          * @param {String} selector The CSS selector
7068          * @return {Array} An array of the matched nodes
7069          */
7070         query : function(selector, unique){
7071             return Roo.DomQuery.select(selector, this.dom);
7072         },
7073
7074         /**
7075          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7076          * @param {String} selector The CSS selector
7077          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7078          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7079          */
7080         child : function(selector, returnDom){
7081             var n = Roo.DomQuery.selectNode(selector, this.dom);
7082             return returnDom ? n : Roo.get(n);
7083         },
7084
7085         /**
7086          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7087          * @param {String} selector The CSS selector
7088          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7089          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7090          */
7091         down : function(selector, returnDom){
7092             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7093             return returnDom ? n : Roo.get(n);
7094         },
7095
7096         /**
7097          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7098          * @param {String} group The group the DD object is member of
7099          * @param {Object} config The DD config object
7100          * @param {Object} overrides An object containing methods to override/implement on the DD object
7101          * @return {Roo.dd.DD} The DD object
7102          */
7103         initDD : function(group, config, overrides){
7104             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7105             return Roo.apply(dd, overrides);
7106         },
7107
7108         /**
7109          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7110          * @param {String} group The group the DDProxy object is member of
7111          * @param {Object} config The DDProxy config object
7112          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7113          * @return {Roo.dd.DDProxy} The DDProxy object
7114          */
7115         initDDProxy : function(group, config, overrides){
7116             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7117             return Roo.apply(dd, overrides);
7118         },
7119
7120         /**
7121          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7122          * @param {String} group The group the DDTarget object is member of
7123          * @param {Object} config The DDTarget config object
7124          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7125          * @return {Roo.dd.DDTarget} The DDTarget object
7126          */
7127         initDDTarget : function(group, config, overrides){
7128             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7129             return Roo.apply(dd, overrides);
7130         },
7131
7132         /**
7133          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7134          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7135          * @param {Boolean} visible Whether the element is visible
7136          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7137          * @return {Roo.Element} this
7138          */
7139          setVisible : function(visible, animate){
7140             if(!animate || !A){
7141                 if(this.visibilityMode == El.DISPLAY){
7142                     this.setDisplayed(visible);
7143                 }else{
7144                     this.fixDisplay();
7145                     this.dom.style.visibility = visible ? "visible" : "hidden";
7146                 }
7147             }else{
7148                 // closure for composites
7149                 var dom = this.dom;
7150                 var visMode = this.visibilityMode;
7151                 if(visible){
7152                     this.setOpacity(.01);
7153                     this.setVisible(true);
7154                 }
7155                 this.anim({opacity: { to: (visible?1:0) }},
7156                       this.preanim(arguments, 1),
7157                       null, .35, 'easeIn', function(){
7158                          if(!visible){
7159                              if(visMode == El.DISPLAY){
7160                                  dom.style.display = "none";
7161                              }else{
7162                                  dom.style.visibility = "hidden";
7163                              }
7164                              Roo.get(dom).setOpacity(1);
7165                          }
7166                      });
7167             }
7168             return this;
7169         },
7170
7171         /**
7172          * Returns true if display is not "none"
7173          * @return {Boolean}
7174          */
7175         isDisplayed : function() {
7176             return this.getStyle("display") != "none";
7177         },
7178
7179         /**
7180          * Toggles the element's visibility or display, depending on visibility mode.
7181          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7182          * @return {Roo.Element} this
7183          */
7184         toggle : function(animate){
7185             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7186             return this;
7187         },
7188
7189         /**
7190          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7191          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7192          * @return {Roo.Element} this
7193          */
7194         setDisplayed : function(value) {
7195             if(typeof value == "boolean"){
7196                value = value ? this.originalDisplay : "none";
7197             }
7198             this.setStyle("display", value);
7199             return this;
7200         },
7201
7202         /**
7203          * Tries to focus the element. Any exceptions are caught and ignored.
7204          * @return {Roo.Element} this
7205          */
7206         focus : function() {
7207             try{
7208                 this.dom.focus();
7209             }catch(e){}
7210             return this;
7211         },
7212
7213         /**
7214          * Tries to blur the element. Any exceptions are caught and ignored.
7215          * @return {Roo.Element} this
7216          */
7217         blur : function() {
7218             try{
7219                 this.dom.blur();
7220             }catch(e){}
7221             return this;
7222         },
7223
7224         /**
7225          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7226          * @param {String/Array} className The CSS class to add, or an array of classes
7227          * @return {Roo.Element} this
7228          */
7229         addClass : function(className){
7230             if(className instanceof Array){
7231                 for(var i = 0, len = className.length; i < len; i++) {
7232                     this.addClass(className[i]);
7233                 }
7234             }else{
7235                 if(className && !this.hasClass(className)){
7236                     this.dom.className = this.dom.className + " " + className;
7237                 }
7238             }
7239             return this;
7240         },
7241
7242         /**
7243          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7244          * @param {String/Array} className The CSS class to add, or an array of classes
7245          * @return {Roo.Element} this
7246          */
7247         radioClass : function(className){
7248             var siblings = this.dom.parentNode.childNodes;
7249             for(var i = 0; i < siblings.length; i++) {
7250                 var s = siblings[i];
7251                 if(s.nodeType == 1){
7252                     Roo.get(s).removeClass(className);
7253                 }
7254             }
7255             this.addClass(className);
7256             return this;
7257         },
7258
7259         /**
7260          * Removes one or more CSS classes from the element.
7261          * @param {String/Array} className The CSS class to remove, or an array of classes
7262          * @return {Roo.Element} this
7263          */
7264         removeClass : function(className){
7265             if(!className || !this.dom.className){
7266                 return this;
7267             }
7268             if(className instanceof Array){
7269                 for(var i = 0, len = className.length; i < len; i++) {
7270                     this.removeClass(className[i]);
7271                 }
7272             }else{
7273                 if(this.hasClass(className)){
7274                     var re = this.classReCache[className];
7275                     if (!re) {
7276                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7277                        this.classReCache[className] = re;
7278                     }
7279                     this.dom.className =
7280                         this.dom.className.replace(re, " ");
7281                 }
7282             }
7283             return this;
7284         },
7285
7286         // private
7287         classReCache: {},
7288
7289         /**
7290          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7291          * @param {String} className The CSS class to toggle
7292          * @return {Roo.Element} this
7293          */
7294         toggleClass : function(className){
7295             if(this.hasClass(className)){
7296                 this.removeClass(className);
7297             }else{
7298                 this.addClass(className);
7299             }
7300             return this;
7301         },
7302
7303         /**
7304          * Checks if the specified CSS class exists on this element's DOM node.
7305          * @param {String} className The CSS class to check for
7306          * @return {Boolean} True if the class exists, else false
7307          */
7308         hasClass : function(className){
7309             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7310         },
7311
7312         /**
7313          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7314          * @param {String} oldClassName The CSS class to replace
7315          * @param {String} newClassName The replacement CSS class
7316          * @return {Roo.Element} this
7317          */
7318         replaceClass : function(oldClassName, newClassName){
7319             this.removeClass(oldClassName);
7320             this.addClass(newClassName);
7321             return this;
7322         },
7323
7324         /**
7325          * Returns an object with properties matching the styles requested.
7326          * For example, el.getStyles('color', 'font-size', 'width') might return
7327          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7328          * @param {String} style1 A style name
7329          * @param {String} style2 A style name
7330          * @param {String} etc.
7331          * @return {Object} The style object
7332          */
7333         getStyles : function(){
7334             var a = arguments, len = a.length, r = {};
7335             for(var i = 0; i < len; i++){
7336                 r[a[i]] = this.getStyle(a[i]);
7337             }
7338             return r;
7339         },
7340
7341         /**
7342          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7343          * @param {String} property The style property whose value is returned.
7344          * @return {String} The current value of the style property for this element.
7345          */
7346         getStyle : function(){
7347             return view && view.getComputedStyle ?
7348                 function(prop){
7349                     var el = this.dom, v, cs, camel;
7350                     if(prop == 'float'){
7351                         prop = "cssFloat";
7352                     }
7353                     if(el.style && (v = el.style[prop])){
7354                         return v;
7355                     }
7356                     if(cs = view.getComputedStyle(el, "")){
7357                         if(!(camel = propCache[prop])){
7358                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7359                         }
7360                         return cs[camel];
7361                     }
7362                     return null;
7363                 } :
7364                 function(prop){
7365                     var el = this.dom, v, cs, camel;
7366                     if(prop == 'opacity'){
7367                         if(typeof el.style.filter == 'string'){
7368                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7369                             if(m){
7370                                 var fv = parseFloat(m[1]);
7371                                 if(!isNaN(fv)){
7372                                     return fv ? fv / 100 : 0;
7373                                 }
7374                             }
7375                         }
7376                         return 1;
7377                     }else if(prop == 'float'){
7378                         prop = "styleFloat";
7379                     }
7380                     if(!(camel = propCache[prop])){
7381                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7382                     }
7383                     if(v = el.style[camel]){
7384                         return v;
7385                     }
7386                     if(cs = el.currentStyle){
7387                         return cs[camel];
7388                     }
7389                     return null;
7390                 };
7391         }(),
7392
7393         /**
7394          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7395          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7396          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7397          * @return {Roo.Element} this
7398          */
7399         setStyle : function(prop, value){
7400             if(typeof prop == "string"){
7401                 
7402                 if (prop == 'float') {
7403                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7404                     return this;
7405                 }
7406                 
7407                 var camel;
7408                 if(!(camel = propCache[prop])){
7409                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7410                 }
7411                 
7412                 if(camel == 'opacity') {
7413                     this.setOpacity(value);
7414                 }else{
7415                     this.dom.style[camel] = value;
7416                 }
7417             }else{
7418                 for(var style in prop){
7419                     if(typeof prop[style] != "function"){
7420                        this.setStyle(style, prop[style]);
7421                     }
7422                 }
7423             }
7424             return this;
7425         },
7426
7427         /**
7428          * More flexible version of {@link #setStyle} for setting style properties.
7429          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7430          * a function which returns such a specification.
7431          * @return {Roo.Element} this
7432          */
7433         applyStyles : function(style){
7434             Roo.DomHelper.applyStyles(this.dom, style);
7435             return this;
7436         },
7437
7438         /**
7439           * 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).
7440           * @return {Number} The X position of the element
7441           */
7442         getX : function(){
7443             return D.getX(this.dom);
7444         },
7445
7446         /**
7447           * 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).
7448           * @return {Number} The Y position of the element
7449           */
7450         getY : function(){
7451             return D.getY(this.dom);
7452         },
7453
7454         /**
7455           * 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).
7456           * @return {Array} The XY position of the element
7457           */
7458         getXY : function(){
7459             return D.getXY(this.dom);
7460         },
7461
7462         /**
7463          * 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).
7464          * @param {Number} The X position of the element
7465          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7466          * @return {Roo.Element} this
7467          */
7468         setX : function(x, animate){
7469             if(!animate || !A){
7470                 D.setX(this.dom, x);
7471             }else{
7472                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7473             }
7474             return this;
7475         },
7476
7477         /**
7478          * 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).
7479          * @param {Number} The Y position of the element
7480          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7481          * @return {Roo.Element} this
7482          */
7483         setY : function(y, animate){
7484             if(!animate || !A){
7485                 D.setY(this.dom, y);
7486             }else{
7487                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7488             }
7489             return this;
7490         },
7491
7492         /**
7493          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7494          * @param {String} left The left CSS property value
7495          * @return {Roo.Element} this
7496          */
7497         setLeft : function(left){
7498             this.setStyle("left", this.addUnits(left));
7499             return this;
7500         },
7501
7502         /**
7503          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7504          * @param {String} top The top CSS property value
7505          * @return {Roo.Element} this
7506          */
7507         setTop : function(top){
7508             this.setStyle("top", this.addUnits(top));
7509             return this;
7510         },
7511
7512         /**
7513          * Sets the element's CSS right style.
7514          * @param {String} right The right CSS property value
7515          * @return {Roo.Element} this
7516          */
7517         setRight : function(right){
7518             this.setStyle("right", this.addUnits(right));
7519             return this;
7520         },
7521
7522         /**
7523          * Sets the element's CSS bottom style.
7524          * @param {String} bottom The bottom CSS property value
7525          * @return {Roo.Element} this
7526          */
7527         setBottom : function(bottom){
7528             this.setStyle("bottom", this.addUnits(bottom));
7529             return this;
7530         },
7531
7532         /**
7533          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7534          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7535          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7536          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7537          * @return {Roo.Element} this
7538          */
7539         setXY : function(pos, animate){
7540             if(!animate || !A){
7541                 D.setXY(this.dom, pos);
7542             }else{
7543                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7544             }
7545             return this;
7546         },
7547
7548         /**
7549          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7550          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7551          * @param {Number} x X value for new position (coordinates are page-based)
7552          * @param {Number} y Y value 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         setLocation : function(x, y, animate){
7557             this.setXY([x, y], this.preanim(arguments, 2));
7558             return this;
7559         },
7560
7561         /**
7562          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7563          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7564          * @param {Number} x X value for new position (coordinates are page-based)
7565          * @param {Number} y Y value for new position (coordinates are page-based)
7566          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7567          * @return {Roo.Element} this
7568          */
7569         moveTo : function(x, y, animate){
7570             this.setXY([x, y], this.preanim(arguments, 2));
7571             return this;
7572         },
7573
7574         /**
7575          * Returns the region of the given element.
7576          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7577          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7578          */
7579         getRegion : function(){
7580             return D.getRegion(this.dom);
7581         },
7582
7583         /**
7584          * Returns the offset height of the element
7585          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7586          * @return {Number} The element's height
7587          */
7588         getHeight : function(contentHeight){
7589             var h = this.dom.offsetHeight || 0;
7590             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7591         },
7592
7593         /**
7594          * Returns the offset width of the element
7595          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7596          * @return {Number} The element's width
7597          */
7598         getWidth : function(contentWidth){
7599             var w = this.dom.offsetWidth || 0;
7600             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7601         },
7602
7603         /**
7604          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7605          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7606          * if a height has not been set using CSS.
7607          * @return {Number}
7608          */
7609         getComputedHeight : function(){
7610             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7611             if(!h){
7612                 h = parseInt(this.getStyle('height'), 10) || 0;
7613                 if(!this.isBorderBox()){
7614                     h += this.getFrameWidth('tb');
7615                 }
7616             }
7617             return h;
7618         },
7619
7620         /**
7621          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7622          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7623          * if a width has not been set using CSS.
7624          * @return {Number}
7625          */
7626         getComputedWidth : function(){
7627             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7628             if(!w){
7629                 w = parseInt(this.getStyle('width'), 10) || 0;
7630                 if(!this.isBorderBox()){
7631                     w += this.getFrameWidth('lr');
7632                 }
7633             }
7634             return w;
7635         },
7636
7637         /**
7638          * Returns the size of the element.
7639          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7640          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7641          */
7642         getSize : function(contentSize){
7643             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7644         },
7645
7646         /**
7647          * Returns the width and height of the viewport.
7648          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7649          */
7650         getViewSize : function(){
7651             var d = this.dom, doc = document, aw = 0, ah = 0;
7652             if(d == doc || d == doc.body){
7653                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7654             }else{
7655                 return {
7656                     width : d.clientWidth,
7657                     height: d.clientHeight
7658                 };
7659             }
7660         },
7661
7662         /**
7663          * Returns the value of the "value" attribute
7664          * @param {Boolean} asNumber true to parse the value as a number
7665          * @return {String/Number}
7666          */
7667         getValue : function(asNumber){
7668             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7669         },
7670
7671         // private
7672         adjustWidth : function(width){
7673             if(typeof width == "number"){
7674                 if(this.autoBoxAdjust && !this.isBorderBox()){
7675                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7676                 }
7677                 if(width < 0){
7678                     width = 0;
7679                 }
7680             }
7681             return width;
7682         },
7683
7684         // private
7685         adjustHeight : function(height){
7686             if(typeof height == "number"){
7687                if(this.autoBoxAdjust && !this.isBorderBox()){
7688                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7689                }
7690                if(height < 0){
7691                    height = 0;
7692                }
7693             }
7694             return height;
7695         },
7696
7697         /**
7698          * Set the width of the element
7699          * @param {Number} width The new width
7700          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7701          * @return {Roo.Element} this
7702          */
7703         setWidth : function(width, animate){
7704             width = this.adjustWidth(width);
7705             if(!animate || !A){
7706                 this.dom.style.width = this.addUnits(width);
7707             }else{
7708                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7709             }
7710             return this;
7711         },
7712
7713         /**
7714          * Set the height of the element
7715          * @param {Number} height The new height
7716          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7717          * @return {Roo.Element} this
7718          */
7719          setHeight : function(height, animate){
7720             height = this.adjustHeight(height);
7721             if(!animate || !A){
7722                 this.dom.style.height = this.addUnits(height);
7723             }else{
7724                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7725             }
7726             return this;
7727         },
7728
7729         /**
7730          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7731          * @param {Number} width The new width
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          setSize : function(width, height, animate){
7737             if(typeof width == "object"){ // in case of object from getSize()
7738                 height = width.height; width = width.width;
7739             }
7740             width = this.adjustWidth(width); height = this.adjustHeight(height);
7741             if(!animate || !A){
7742                 this.dom.style.width = this.addUnits(width);
7743                 this.dom.style.height = this.addUnits(height);
7744             }else{
7745                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7746             }
7747             return this;
7748         },
7749
7750         /**
7751          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7752          * @param {Number} x X value for new position (coordinates are page-based)
7753          * @param {Number} y Y value for new position (coordinates are page-based)
7754          * @param {Number} width The new width
7755          * @param {Number} height The new height
7756          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7757          * @return {Roo.Element} this
7758          */
7759         setBounds : function(x, y, width, height, animate){
7760             if(!animate || !A){
7761                 this.setSize(width, height);
7762                 this.setLocation(x, y);
7763             }else{
7764                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7765                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7766                               this.preanim(arguments, 4), 'motion');
7767             }
7768             return this;
7769         },
7770
7771         /**
7772          * 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.
7773          * @param {Roo.lib.Region} region The region to fill
7774          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7775          * @return {Roo.Element} this
7776          */
7777         setRegion : function(region, animate){
7778             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7779             return this;
7780         },
7781
7782         /**
7783          * Appends an event handler
7784          *
7785          * @param {String}   eventName     The type of event to append
7786          * @param {Function} fn        The method the event invokes
7787          * @param {Object} scope       (optional) The scope (this object) of the fn
7788          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7789          */
7790         addListener : function(eventName, fn, scope, options){
7791             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7792         },
7793
7794         /**
7795          * Removes an event handler from this element
7796          * @param {String} eventName the type of event to remove
7797          * @param {Function} fn the method the event invokes
7798          * @return {Roo.Element} this
7799          */
7800         removeListener : function(eventName, fn){
7801             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7802             return this;
7803         },
7804
7805         /**
7806          * Removes all previous added listeners from this element
7807          * @return {Roo.Element} this
7808          */
7809         removeAllListeners : function(){
7810             E.purgeElement(this.dom);
7811             return this;
7812         },
7813
7814         relayEvent : function(eventName, observable){
7815             this.on(eventName, function(e){
7816                 observable.fireEvent(eventName, e);
7817             });
7818         },
7819
7820         /**
7821          * Set the opacity of the element
7822          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7823          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7824          * @return {Roo.Element} this
7825          */
7826          setOpacity : function(opacity, animate){
7827             if(!animate || !A){
7828                 var s = this.dom.style;
7829                 if(Roo.isIE){
7830                     s.zoom = 1;
7831                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7832                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7833                 }else{
7834                     s.opacity = opacity;
7835                 }
7836             }else{
7837                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7838             }
7839             return this;
7840         },
7841
7842         /**
7843          * Gets the left X coordinate
7844          * @param {Boolean} local True to get the local css position instead of page coordinate
7845          * @return {Number}
7846          */
7847         getLeft : function(local){
7848             if(!local){
7849                 return this.getX();
7850             }else{
7851                 return parseInt(this.getStyle("left"), 10) || 0;
7852             }
7853         },
7854
7855         /**
7856          * Gets the right X coordinate of the element (element X position + element width)
7857          * @param {Boolean} local True to get the local css position instead of page coordinate
7858          * @return {Number}
7859          */
7860         getRight : function(local){
7861             if(!local){
7862                 return this.getX() + this.getWidth();
7863             }else{
7864                 return (this.getLeft(true) + this.getWidth()) || 0;
7865             }
7866         },
7867
7868         /**
7869          * Gets the top Y coordinate
7870          * @param {Boolean} local True to get the local css position instead of page coordinate
7871          * @return {Number}
7872          */
7873         getTop : function(local) {
7874             if(!local){
7875                 return this.getY();
7876             }else{
7877                 return parseInt(this.getStyle("top"), 10) || 0;
7878             }
7879         },
7880
7881         /**
7882          * Gets the bottom Y coordinate of the element (element Y position + element height)
7883          * @param {Boolean} local True to get the local css position instead of page coordinate
7884          * @return {Number}
7885          */
7886         getBottom : function(local){
7887             if(!local){
7888                 return this.getY() + this.getHeight();
7889             }else{
7890                 return (this.getTop(true) + this.getHeight()) || 0;
7891             }
7892         },
7893
7894         /**
7895         * Initializes positioning on this element. If a desired position is not passed, it will make the
7896         * the element positioned relative IF it is not already positioned.
7897         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7898         * @param {Number} zIndex (optional) The zIndex to apply
7899         * @param {Number} x (optional) Set the page X position
7900         * @param {Number} y (optional) Set the page Y position
7901         */
7902         position : function(pos, zIndex, x, y){
7903             if(!pos){
7904                if(this.getStyle('position') == 'static'){
7905                    this.setStyle('position', 'relative');
7906                }
7907             }else{
7908                 this.setStyle("position", pos);
7909             }
7910             if(zIndex){
7911                 this.setStyle("z-index", zIndex);
7912             }
7913             if(x !== undefined && y !== undefined){
7914                 this.setXY([x, y]);
7915             }else if(x !== undefined){
7916                 this.setX(x);
7917             }else if(y !== undefined){
7918                 this.setY(y);
7919             }
7920         },
7921
7922         /**
7923         * Clear positioning back to the default when the document was loaded
7924         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7925         * @return {Roo.Element} this
7926          */
7927         clearPositioning : function(value){
7928             value = value ||'';
7929             this.setStyle({
7930                 "left": value,
7931                 "right": value,
7932                 "top": value,
7933                 "bottom": value,
7934                 "z-index": "",
7935                 "position" : "static"
7936             });
7937             return this;
7938         },
7939
7940         /**
7941         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7942         * snapshot before performing an update and then restoring the element.
7943         * @return {Object}
7944         */
7945         getPositioning : function(){
7946             var l = this.getStyle("left");
7947             var t = this.getStyle("top");
7948             return {
7949                 "position" : this.getStyle("position"),
7950                 "left" : l,
7951                 "right" : l ? "" : this.getStyle("right"),
7952                 "top" : t,
7953                 "bottom" : t ? "" : this.getStyle("bottom"),
7954                 "z-index" : this.getStyle("z-index")
7955             };
7956         },
7957
7958         /**
7959          * Gets the width of the border(s) for the specified side(s)
7960          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7961          * passing lr would get the border (l)eft width + the border (r)ight width.
7962          * @return {Number} The width of the sides passed added together
7963          */
7964         getBorderWidth : function(side){
7965             return this.addStyles(side, El.borders);
7966         },
7967
7968         /**
7969          * Gets the width of the padding(s) for the specified side(s)
7970          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7971          * passing lr would get the padding (l)eft + the padding (r)ight.
7972          * @return {Number} The padding of the sides passed added together
7973          */
7974         getPadding : function(side){
7975             return this.addStyles(side, El.paddings);
7976         },
7977
7978         /**
7979         * Set positioning with an object returned by getPositioning().
7980         * @param {Object} posCfg
7981         * @return {Roo.Element} this
7982          */
7983         setPositioning : function(pc){
7984             this.applyStyles(pc);
7985             if(pc.right == "auto"){
7986                 this.dom.style.right = "";
7987             }
7988             if(pc.bottom == "auto"){
7989                 this.dom.style.bottom = "";
7990             }
7991             return this;
7992         },
7993
7994         // private
7995         fixDisplay : function(){
7996             if(this.getStyle("display") == "none"){
7997                 this.setStyle("visibility", "hidden");
7998                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7999                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8000                     this.setStyle("display", "block");
8001                 }
8002             }
8003         },
8004
8005         /**
8006          * Quick set left and top adding default units
8007          * @param {String} left The left CSS property value
8008          * @param {String} top The top CSS property value
8009          * @return {Roo.Element} this
8010          */
8011          setLeftTop : function(left, top){
8012             this.dom.style.left = this.addUnits(left);
8013             this.dom.style.top = this.addUnits(top);
8014             return this;
8015         },
8016
8017         /**
8018          * Move this element relative to its current position.
8019          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8020          * @param {Number} distance How far to move the element in pixels
8021          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8022          * @return {Roo.Element} this
8023          */
8024          move : function(direction, distance, animate){
8025             var xy = this.getXY();
8026             direction = direction.toLowerCase();
8027             switch(direction){
8028                 case "l":
8029                 case "left":
8030                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8031                     break;
8032                case "r":
8033                case "right":
8034                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8035                     break;
8036                case "t":
8037                case "top":
8038                case "up":
8039                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8040                     break;
8041                case "b":
8042                case "bottom":
8043                case "down":
8044                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8045                     break;
8046             }
8047             return this;
8048         },
8049
8050         /**
8051          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8052          * @return {Roo.Element} this
8053          */
8054         clip : function(){
8055             if(!this.isClipped){
8056                this.isClipped = true;
8057                this.originalClip = {
8058                    "o": this.getStyle("overflow"),
8059                    "x": this.getStyle("overflow-x"),
8060                    "y": this.getStyle("overflow-y")
8061                };
8062                this.setStyle("overflow", "hidden");
8063                this.setStyle("overflow-x", "hidden");
8064                this.setStyle("overflow-y", "hidden");
8065             }
8066             return this;
8067         },
8068
8069         /**
8070          *  Return clipping (overflow) to original clipping before clip() was called
8071          * @return {Roo.Element} this
8072          */
8073         unclip : function(){
8074             if(this.isClipped){
8075                 this.isClipped = false;
8076                 var o = this.originalClip;
8077                 if(o.o){this.setStyle("overflow", o.o);}
8078                 if(o.x){this.setStyle("overflow-x", o.x);}
8079                 if(o.y){this.setStyle("overflow-y", o.y);}
8080             }
8081             return this;
8082         },
8083
8084
8085         /**
8086          * Gets the x,y coordinates specified by the anchor position on the element.
8087          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8088          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8089          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8090          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8091          * @return {Array} [x, y] An array containing the element's x and y coordinates
8092          */
8093         getAnchorXY : function(anchor, local, s){
8094             //Passing a different size is useful for pre-calculating anchors,
8095             //especially for anchored animations that change the el size.
8096
8097             var w, h, vp = false;
8098             if(!s){
8099                 var d = this.dom;
8100                 if(d == document.body || d == document){
8101                     vp = true;
8102                     w = D.getViewWidth(); h = D.getViewHeight();
8103                 }else{
8104                     w = this.getWidth(); h = this.getHeight();
8105                 }
8106             }else{
8107                 w = s.width;  h = s.height;
8108             }
8109             var x = 0, y = 0, r = Math.round;
8110             switch((anchor || "tl").toLowerCase()){
8111                 case "c":
8112                     x = r(w*.5);
8113                     y = r(h*.5);
8114                 break;
8115                 case "t":
8116                     x = r(w*.5);
8117                     y = 0;
8118                 break;
8119                 case "l":
8120                     x = 0;
8121                     y = r(h*.5);
8122                 break;
8123                 case "r":
8124                     x = w;
8125                     y = r(h*.5);
8126                 break;
8127                 case "b":
8128                     x = r(w*.5);
8129                     y = h;
8130                 break;
8131                 case "tl":
8132                     x = 0;
8133                     y = 0;
8134                 break;
8135                 case "bl":
8136                     x = 0;
8137                     y = h;
8138                 break;
8139                 case "br":
8140                     x = w;
8141                     y = h;
8142                 break;
8143                 case "tr":
8144                     x = w;
8145                     y = 0;
8146                 break;
8147             }
8148             if(local === true){
8149                 return [x, y];
8150             }
8151             if(vp){
8152                 var sc = this.getScroll();
8153                 return [x + sc.left, y + sc.top];
8154             }
8155             //Add the element's offset xy
8156             var o = this.getXY();
8157             return [x+o[0], y+o[1]];
8158         },
8159
8160         /**
8161          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8162          * supported position values.
8163          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8164          * @param {String} position The position to align to.
8165          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8166          * @return {Array} [x, y]
8167          */
8168         getAlignToXY : function(el, p, o){
8169             el = Roo.get(el);
8170             var d = this.dom;
8171             if(!el.dom){
8172                 throw "Element.alignTo with an element that doesn't exist";
8173             }
8174             var c = false; //constrain to viewport
8175             var p1 = "", p2 = "";
8176             o = o || [0,0];
8177
8178             if(!p){
8179                 p = "tl-bl";
8180             }else if(p == "?"){
8181                 p = "tl-bl?";
8182             }else if(p.indexOf("-") == -1){
8183                 p = "tl-" + p;
8184             }
8185             p = p.toLowerCase();
8186             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8187             if(!m){
8188                throw "Element.alignTo with an invalid alignment " + p;
8189             }
8190             p1 = m[1]; p2 = m[2]; c = !!m[3];
8191
8192             //Subtract the aligned el's internal xy from the target's offset xy
8193             //plus custom offset to get the aligned el's new offset xy
8194             var a1 = this.getAnchorXY(p1, true);
8195             var a2 = el.getAnchorXY(p2, false);
8196             var x = a2[0] - a1[0] + o[0];
8197             var y = a2[1] - a1[1] + o[1];
8198             if(c){
8199                 //constrain the aligned el to viewport if necessary
8200                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8201                 // 5px of margin for ie
8202                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8203
8204                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8205                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8206                 //otherwise swap the aligned el to the opposite border of the target.
8207                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8208                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8209                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8210                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8211
8212                var doc = document;
8213                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8214                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8215
8216                if((x+w) > dw + scrollX){
8217                     x = swapX ? r.left-w : dw+scrollX-w;
8218                 }
8219                if(x < scrollX){
8220                    x = swapX ? r.right : scrollX;
8221                }
8222                if((y+h) > dh + scrollY){
8223                     y = swapY ? r.top-h : dh+scrollY-h;
8224                 }
8225                if (y < scrollY){
8226                    y = swapY ? r.bottom : scrollY;
8227                }
8228             }
8229             return [x,y];
8230         },
8231
8232         // private
8233         getConstrainToXY : function(){
8234             var os = {top:0, left:0, bottom:0, right: 0};
8235
8236             return function(el, local, offsets, proposedXY){
8237                 el = Roo.get(el);
8238                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8239
8240                 var vw, vh, vx = 0, vy = 0;
8241                 if(el.dom == document.body || el.dom == document){
8242                     vw = Roo.lib.Dom.getViewWidth();
8243                     vh = Roo.lib.Dom.getViewHeight();
8244                 }else{
8245                     vw = el.dom.clientWidth;
8246                     vh = el.dom.clientHeight;
8247                     if(!local){
8248                         var vxy = el.getXY();
8249                         vx = vxy[0];
8250                         vy = vxy[1];
8251                     }
8252                 }
8253
8254                 var s = el.getScroll();
8255
8256                 vx += offsets.left + s.left;
8257                 vy += offsets.top + s.top;
8258
8259                 vw -= offsets.right;
8260                 vh -= offsets.bottom;
8261
8262                 var vr = vx+vw;
8263                 var vb = vy+vh;
8264
8265                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8266                 var x = xy[0], y = xy[1];
8267                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8268
8269                 // only move it if it needs it
8270                 var moved = false;
8271
8272                 // first validate right/bottom
8273                 if((x + w) > vr){
8274                     x = vr - w;
8275                     moved = true;
8276                 }
8277                 if((y + h) > vb){
8278                     y = vb - h;
8279                     moved = true;
8280                 }
8281                 // then make sure top/left isn't negative
8282                 if(x < vx){
8283                     x = vx;
8284                     moved = true;
8285                 }
8286                 if(y < vy){
8287                     y = vy;
8288                     moved = true;
8289                 }
8290                 return moved ? [x, y] : false;
8291             };
8292         }(),
8293
8294         // private
8295         adjustForConstraints : function(xy, parent, offsets){
8296             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8297         },
8298
8299         /**
8300          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8301          * document it aligns it to the viewport.
8302          * The position parameter is optional, and can be specified in any one of the following formats:
8303          * <ul>
8304          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8305          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8306          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8307          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8308          *   <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
8309          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8310          * </ul>
8311          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8312          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8313          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8314          * that specified in order to enforce the viewport constraints.
8315          * Following are all of the supported anchor positions:
8316     <pre>
8317     Value  Description
8318     -----  -----------------------------
8319     tl     The top left corner (default)
8320     t      The center of the top edge
8321     tr     The top right corner
8322     l      The center of the left edge
8323     c      In the center of the element
8324     r      The center of the right edge
8325     bl     The bottom left corner
8326     b      The center of the bottom edge
8327     br     The bottom right corner
8328     </pre>
8329     Example Usage:
8330     <pre><code>
8331     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8332     el.alignTo("other-el");
8333
8334     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8335     el.alignTo("other-el", "tr?");
8336
8337     // align the bottom right corner of el with the center left edge of other-el
8338     el.alignTo("other-el", "br-l?");
8339
8340     // align the center of el with the bottom left corner of other-el and
8341     // adjust the x position by -6 pixels (and the y position by 0)
8342     el.alignTo("other-el", "c-bl", [-6, 0]);
8343     </code></pre>
8344          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8345          * @param {String} position The position to align to.
8346          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8347          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8348          * @return {Roo.Element} this
8349          */
8350         alignTo : function(element, position, offsets, animate){
8351             var xy = this.getAlignToXY(element, position, offsets);
8352             this.setXY(xy, this.preanim(arguments, 3));
8353             return this;
8354         },
8355
8356         /**
8357          * Anchors an element to another element and realigns it when the window is resized.
8358          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8359          * @param {String} position The position to align to.
8360          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8361          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8362          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8363          * is a number, it is used as the buffer delay (defaults to 50ms).
8364          * @param {Function} callback The function to call after the animation finishes
8365          * @return {Roo.Element} this
8366          */
8367         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8368             var action = function(){
8369                 this.alignTo(el, alignment, offsets, animate);
8370                 Roo.callback(callback, this);
8371             };
8372             Roo.EventManager.onWindowResize(action, this);
8373             var tm = typeof monitorScroll;
8374             if(tm != 'undefined'){
8375                 Roo.EventManager.on(window, 'scroll', action, this,
8376                     {buffer: tm == 'number' ? monitorScroll : 50});
8377             }
8378             action.call(this); // align immediately
8379             return this;
8380         },
8381         /**
8382          * Clears any opacity settings from this element. Required in some cases for IE.
8383          * @return {Roo.Element} this
8384          */
8385         clearOpacity : function(){
8386             if (window.ActiveXObject) {
8387                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8388                     this.dom.style.filter = "";
8389                 }
8390             } else {
8391                 this.dom.style.opacity = "";
8392                 this.dom.style["-moz-opacity"] = "";
8393                 this.dom.style["-khtml-opacity"] = "";
8394             }
8395             return this;
8396         },
8397
8398         /**
8399          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8400          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8401          * @return {Roo.Element} this
8402          */
8403         hide : function(animate){
8404             this.setVisible(false, this.preanim(arguments, 0));
8405             return this;
8406         },
8407
8408         /**
8409         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8410         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8411          * @return {Roo.Element} this
8412          */
8413         show : function(animate){
8414             this.setVisible(true, this.preanim(arguments, 0));
8415             return this;
8416         },
8417
8418         /**
8419          * @private Test if size has a unit, otherwise appends the default
8420          */
8421         addUnits : function(size){
8422             return Roo.Element.addUnits(size, this.defaultUnit);
8423         },
8424
8425         /**
8426          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8427          * @return {Roo.Element} this
8428          */
8429         beginMeasure : function(){
8430             var el = this.dom;
8431             if(el.offsetWidth || el.offsetHeight){
8432                 return this; // offsets work already
8433             }
8434             var changed = [];
8435             var p = this.dom, b = document.body; // start with this element
8436             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8437                 var pe = Roo.get(p);
8438                 if(pe.getStyle('display') == 'none'){
8439                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8440                     p.style.visibility = "hidden";
8441                     p.style.display = "block";
8442                 }
8443                 p = p.parentNode;
8444             }
8445             this._measureChanged = changed;
8446             return this;
8447
8448         },
8449
8450         /**
8451          * Restores displays to before beginMeasure was called
8452          * @return {Roo.Element} this
8453          */
8454         endMeasure : function(){
8455             var changed = this._measureChanged;
8456             if(changed){
8457                 for(var i = 0, len = changed.length; i < len; i++) {
8458                     var r = changed[i];
8459                     r.el.style.visibility = r.visibility;
8460                     r.el.style.display = "none";
8461                 }
8462                 this._measureChanged = null;
8463             }
8464             return this;
8465         },
8466
8467         /**
8468         * Update the innerHTML of this element, optionally searching for and processing scripts
8469         * @param {String} html The new HTML
8470         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8471         * @param {Function} callback For async script loading you can be noticed when the update completes
8472         * @return {Roo.Element} this
8473          */
8474         update : function(html, loadScripts, callback){
8475             if(typeof html == "undefined"){
8476                 html = "";
8477             }
8478             if(loadScripts !== true){
8479                 this.dom.innerHTML = html;
8480                 if(typeof callback == "function"){
8481                     callback();
8482                 }
8483                 return this;
8484             }
8485             var id = Roo.id();
8486             var dom = this.dom;
8487
8488             html += '<span id="' + id + '"></span>';
8489
8490             E.onAvailable(id, function(){
8491                 var hd = document.getElementsByTagName("head")[0];
8492                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8493                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8494                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8495
8496                 var match;
8497                 while(match = re.exec(html)){
8498                     var attrs = match[1];
8499                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8500                     if(srcMatch && srcMatch[2]){
8501                        var s = document.createElement("script");
8502                        s.src = srcMatch[2];
8503                        var typeMatch = attrs.match(typeRe);
8504                        if(typeMatch && typeMatch[2]){
8505                            s.type = typeMatch[2];
8506                        }
8507                        hd.appendChild(s);
8508                     }else if(match[2] && match[2].length > 0){
8509                         if(window.execScript) {
8510                            window.execScript(match[2]);
8511                         } else {
8512                             /**
8513                              * eval:var:id
8514                              * eval:var:dom
8515                              * eval:var:html
8516                              * 
8517                              */
8518                            window.eval(match[2]);
8519                         }
8520                     }
8521                 }
8522                 var el = document.getElementById(id);
8523                 if(el){el.parentNode.removeChild(el);}
8524                 if(typeof callback == "function"){
8525                     callback();
8526                 }
8527             });
8528             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8529             return this;
8530         },
8531
8532         /**
8533          * Direct access to the UpdateManager update() method (takes the same parameters).
8534          * @param {String/Function} url The url for this request or a function to call to get the url
8535          * @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}
8536          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8537          * @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.
8538          * @return {Roo.Element} this
8539          */
8540         load : function(){
8541             var um = this.getUpdateManager();
8542             um.update.apply(um, arguments);
8543             return this;
8544         },
8545
8546         /**
8547         * Gets this element's UpdateManager
8548         * @return {Roo.UpdateManager} The UpdateManager
8549         */
8550         getUpdateManager : function(){
8551             if(!this.updateManager){
8552                 this.updateManager = new Roo.UpdateManager(this);
8553             }
8554             return this.updateManager;
8555         },
8556
8557         /**
8558          * Disables text selection for this element (normalized across browsers)
8559          * @return {Roo.Element} this
8560          */
8561         unselectable : function(){
8562             this.dom.unselectable = "on";
8563             this.swallowEvent("selectstart", true);
8564             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8565             this.addClass("x-unselectable");
8566             return this;
8567         },
8568
8569         /**
8570         * Calculates the x, y to center this element on the screen
8571         * @return {Array} The x, y values [x, y]
8572         */
8573         getCenterXY : function(){
8574             return this.getAlignToXY(document, 'c-c');
8575         },
8576
8577         /**
8578         * Centers the Element in either the viewport, or another Element.
8579         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8580         */
8581         center : function(centerIn){
8582             this.alignTo(centerIn || document, 'c-c');
8583             return this;
8584         },
8585
8586         /**
8587          * Tests various css rules/browsers to determine if this element uses a border box
8588          * @return {Boolean}
8589          */
8590         isBorderBox : function(){
8591             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8592         },
8593
8594         /**
8595          * Return a box {x, y, width, height} that can be used to set another elements
8596          * size/location to match this element.
8597          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8598          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8599          * @return {Object} box An object in the format {x, y, width, height}
8600          */
8601         getBox : function(contentBox, local){
8602             var xy;
8603             if(!local){
8604                 xy = this.getXY();
8605             }else{
8606                 var left = parseInt(this.getStyle("left"), 10) || 0;
8607                 var top = parseInt(this.getStyle("top"), 10) || 0;
8608                 xy = [left, top];
8609             }
8610             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8611             if(!contentBox){
8612                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8613             }else{
8614                 var l = this.getBorderWidth("l")+this.getPadding("l");
8615                 var r = this.getBorderWidth("r")+this.getPadding("r");
8616                 var t = this.getBorderWidth("t")+this.getPadding("t");
8617                 var b = this.getBorderWidth("b")+this.getPadding("b");
8618                 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)};
8619             }
8620             bx.right = bx.x + bx.width;
8621             bx.bottom = bx.y + bx.height;
8622             return bx;
8623         },
8624
8625         /**
8626          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8627          for more information about the sides.
8628          * @param {String} sides
8629          * @return {Number}
8630          */
8631         getFrameWidth : function(sides, onlyContentBox){
8632             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8633         },
8634
8635         /**
8636          * 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.
8637          * @param {Object} box The box to fill {x, y, width, height}
8638          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8639          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8640          * @return {Roo.Element} this
8641          */
8642         setBox : function(box, adjust, animate){
8643             var w = box.width, h = box.height;
8644             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8645                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8646                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8647             }
8648             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8649             return this;
8650         },
8651
8652         /**
8653          * Forces the browser to repaint this element
8654          * @return {Roo.Element} this
8655          */
8656          repaint : function(){
8657             var dom = this.dom;
8658             this.addClass("x-repaint");
8659             setTimeout(function(){
8660                 Roo.get(dom).removeClass("x-repaint");
8661             }, 1);
8662             return this;
8663         },
8664
8665         /**
8666          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8667          * then it returns the calculated width of the sides (see getPadding)
8668          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8669          * @return {Object/Number}
8670          */
8671         getMargins : function(side){
8672             if(!side){
8673                 return {
8674                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8675                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8676                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8677                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8678                 };
8679             }else{
8680                 return this.addStyles(side, El.margins);
8681              }
8682         },
8683
8684         // private
8685         addStyles : function(sides, styles){
8686             var val = 0, v, w;
8687             for(var i = 0, len = sides.length; i < len; i++){
8688                 v = this.getStyle(styles[sides.charAt(i)]);
8689                 if(v){
8690                      w = parseInt(v, 10);
8691                      if(w){ val += w; }
8692                 }
8693             }
8694             return val;
8695         },
8696
8697         /**
8698          * Creates a proxy element of this element
8699          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8700          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8701          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8702          * @return {Roo.Element} The new proxy element
8703          */
8704         createProxy : function(config, renderTo, matchBox){
8705             if(renderTo){
8706                 renderTo = Roo.getDom(renderTo);
8707             }else{
8708                 renderTo = document.body;
8709             }
8710             config = typeof config == "object" ?
8711                 config : {tag : "div", cls: config};
8712             var proxy = Roo.DomHelper.append(renderTo, config, true);
8713             if(matchBox){
8714                proxy.setBox(this.getBox());
8715             }
8716             return proxy;
8717         },
8718
8719         /**
8720          * Puts a mask over this element to disable user interaction. Requires core.css.
8721          * This method can only be applied to elements which accept child nodes.
8722          * @param {String} msg (optional) A message to display in the mask
8723          * @param {String} msgCls (optional) A css class to apply to the msg element
8724          * @return {Element} The mask  element
8725          */
8726         mask : function(msg, msgCls){
8727             if(this.getStyle("position") == "static"){
8728                 this.setStyle("position", "relative");
8729             }
8730             if(!this._mask){
8731                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8732             }
8733             this.addClass("x-masked");
8734             this._mask.setDisplayed(true);
8735             if(typeof msg == 'string'){
8736                 if(!this._maskMsg){
8737                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8738                 }
8739                 var mm = this._maskMsg;
8740                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8741                 mm.dom.firstChild.innerHTML = msg;
8742                 mm.setDisplayed(true);
8743                 mm.center(this);
8744             }
8745             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8746                 this._mask.setHeight(this.getHeight());
8747             }
8748             return this._mask;
8749         },
8750
8751         /**
8752          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8753          * it is cached for reuse.
8754          */
8755         unmask : function(removeEl){
8756             if(this._mask){
8757                 if(removeEl === true){
8758                     this._mask.remove();
8759                     delete this._mask;
8760                     if(this._maskMsg){
8761                         this._maskMsg.remove();
8762                         delete this._maskMsg;
8763                     }
8764                 }else{
8765                     this._mask.setDisplayed(false);
8766                     if(this._maskMsg){
8767                         this._maskMsg.setDisplayed(false);
8768                     }
8769                 }
8770             }
8771             this.removeClass("x-masked");
8772         },
8773
8774         /**
8775          * Returns true if this element is masked
8776          * @return {Boolean}
8777          */
8778         isMasked : function(){
8779             return this._mask && this._mask.isVisible();
8780         },
8781
8782         /**
8783          * Creates an iframe shim for this element to keep selects and other windowed objects from
8784          * showing through.
8785          * @return {Roo.Element} The new shim element
8786          */
8787         createShim : function(){
8788             var el = document.createElement('iframe');
8789             el.frameBorder = 'no';
8790             el.className = 'roo-shim';
8791             if(Roo.isIE && Roo.isSecure){
8792                 el.src = Roo.SSL_SECURE_URL;
8793             }
8794             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8795             shim.autoBoxAdjust = false;
8796             return shim;
8797         },
8798
8799         /**
8800          * Removes this element from the DOM and deletes it from the cache
8801          */
8802         remove : function(){
8803             if(this.dom.parentNode){
8804                 this.dom.parentNode.removeChild(this.dom);
8805             }
8806             delete El.cache[this.dom.id];
8807         },
8808
8809         /**
8810          * Sets up event handlers to add and remove a css class when the mouse is over this element
8811          * @param {String} className
8812          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8813          * mouseout events for children elements
8814          * @return {Roo.Element} this
8815          */
8816         addClassOnOver : function(className, preventFlicker){
8817             this.on("mouseover", function(){
8818                 Roo.fly(this, '_internal').addClass(className);
8819             }, this.dom);
8820             var removeFn = function(e){
8821                 if(preventFlicker !== true || !e.within(this, true)){
8822                     Roo.fly(this, '_internal').removeClass(className);
8823                 }
8824             };
8825             this.on("mouseout", removeFn, this.dom);
8826             return this;
8827         },
8828
8829         /**
8830          * Sets up event handlers to add and remove a css class when this element has the focus
8831          * @param {String} className
8832          * @return {Roo.Element} this
8833          */
8834         addClassOnFocus : function(className){
8835             this.on("focus", function(){
8836                 Roo.fly(this, '_internal').addClass(className);
8837             }, this.dom);
8838             this.on("blur", function(){
8839                 Roo.fly(this, '_internal').removeClass(className);
8840             }, this.dom);
8841             return this;
8842         },
8843         /**
8844          * 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)
8845          * @param {String} className
8846          * @return {Roo.Element} this
8847          */
8848         addClassOnClick : function(className){
8849             var dom = this.dom;
8850             this.on("mousedown", function(){
8851                 Roo.fly(dom, '_internal').addClass(className);
8852                 var d = Roo.get(document);
8853                 var fn = function(){
8854                     Roo.fly(dom, '_internal').removeClass(className);
8855                     d.removeListener("mouseup", fn);
8856                 };
8857                 d.on("mouseup", fn);
8858             });
8859             return this;
8860         },
8861
8862         /**
8863          * Stops the specified event from bubbling and optionally prevents the default action
8864          * @param {String} eventName
8865          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8866          * @return {Roo.Element} this
8867          */
8868         swallowEvent : function(eventName, preventDefault){
8869             var fn = function(e){
8870                 e.stopPropagation();
8871                 if(preventDefault){
8872                     e.preventDefault();
8873                 }
8874             };
8875             if(eventName instanceof Array){
8876                 for(var i = 0, len = eventName.length; i < len; i++){
8877                      this.on(eventName[i], fn);
8878                 }
8879                 return this;
8880             }
8881             this.on(eventName, fn);
8882             return this;
8883         },
8884
8885         /**
8886          * @private
8887          */
8888       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8889
8890         /**
8891          * Sizes this element to its parent element's dimensions performing
8892          * neccessary box adjustments.
8893          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8894          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8895          * @return {Roo.Element} this
8896          */
8897         fitToParent : function(monitorResize, targetParent) {
8898           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8899           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8900           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8901             return;
8902           }
8903           var p = Roo.get(targetParent || this.dom.parentNode);
8904           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8905           if (monitorResize === true) {
8906             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8907             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8908           }
8909           return this;
8910         },
8911
8912         /**
8913          * Gets the next sibling, skipping text nodes
8914          * @return {HTMLElement} The next sibling or null
8915          */
8916         getNextSibling : function(){
8917             var n = this.dom.nextSibling;
8918             while(n && n.nodeType != 1){
8919                 n = n.nextSibling;
8920             }
8921             return n;
8922         },
8923
8924         /**
8925          * Gets the previous sibling, skipping text nodes
8926          * @return {HTMLElement} The previous sibling or null
8927          */
8928         getPrevSibling : function(){
8929             var n = this.dom.previousSibling;
8930             while(n && n.nodeType != 1){
8931                 n = n.previousSibling;
8932             }
8933             return n;
8934         },
8935
8936
8937         /**
8938          * Appends the passed element(s) to this element
8939          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8940          * @return {Roo.Element} this
8941          */
8942         appendChild: function(el){
8943             el = Roo.get(el);
8944             el.appendTo(this);
8945             return this;
8946         },
8947
8948         /**
8949          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8950          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8951          * automatically generated with the specified attributes.
8952          * @param {HTMLElement} insertBefore (optional) a child element of this element
8953          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8954          * @return {Roo.Element} The new child element
8955          */
8956         createChild: function(config, insertBefore, returnDom){
8957             config = config || {tag:'div'};
8958             if(insertBefore){
8959                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8960             }
8961             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8962         },
8963
8964         /**
8965          * Appends this element to the passed element
8966          * @param {String/HTMLElement/Element} el The new parent element
8967          * @return {Roo.Element} this
8968          */
8969         appendTo: function(el){
8970             el = Roo.getDom(el);
8971             el.appendChild(this.dom);
8972             return this;
8973         },
8974
8975         /**
8976          * Inserts this element before the passed element in the DOM
8977          * @param {String/HTMLElement/Element} el The element to insert before
8978          * @return {Roo.Element} this
8979          */
8980         insertBefore: function(el){
8981             el = Roo.getDom(el);
8982             el.parentNode.insertBefore(this.dom, el);
8983             return this;
8984         },
8985
8986         /**
8987          * Inserts this element after the passed element in the DOM
8988          * @param {String/HTMLElement/Element} el The element to insert after
8989          * @return {Roo.Element} this
8990          */
8991         insertAfter: function(el){
8992             el = Roo.getDom(el);
8993             el.parentNode.insertBefore(this.dom, el.nextSibling);
8994             return this;
8995         },
8996
8997         /**
8998          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8999          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9000          * @return {Roo.Element} The new child
9001          */
9002         insertFirst: function(el, returnDom){
9003             el = el || {};
9004             if(typeof el == 'object' && !el.nodeType){ // dh config
9005                 return this.createChild(el, this.dom.firstChild, returnDom);
9006             }else{
9007                 el = Roo.getDom(el);
9008                 this.dom.insertBefore(el, this.dom.firstChild);
9009                 return !returnDom ? Roo.get(el) : el;
9010             }
9011         },
9012
9013         /**
9014          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9015          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9016          * @param {String} where (optional) 'before' or 'after' defaults to before
9017          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9018          * @return {Roo.Element} the inserted Element
9019          */
9020         insertSibling: function(el, where, returnDom){
9021             where = where ? where.toLowerCase() : 'before';
9022             el = el || {};
9023             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9024
9025             if(typeof el == 'object' && !el.nodeType){ // dh config
9026                 if(where == 'after' && !this.dom.nextSibling){
9027                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9028                 }else{
9029                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9030                 }
9031
9032             }else{
9033                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9034                             where == 'before' ? this.dom : this.dom.nextSibling);
9035                 if(!returnDom){
9036                     rt = Roo.get(rt);
9037                 }
9038             }
9039             return rt;
9040         },
9041
9042         /**
9043          * Creates and wraps this element with another element
9044          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9045          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9046          * @return {HTMLElement/Element} The newly created wrapper element
9047          */
9048         wrap: function(config, returnDom){
9049             if(!config){
9050                 config = {tag: "div"};
9051             }
9052             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9053             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9054             return newEl;
9055         },
9056
9057         /**
9058          * Replaces the passed element with this element
9059          * @param {String/HTMLElement/Element} el The element to replace
9060          * @return {Roo.Element} this
9061          */
9062         replace: function(el){
9063             el = Roo.get(el);
9064             this.insertBefore(el);
9065             el.remove();
9066             return this;
9067         },
9068
9069         /**
9070          * Inserts an html fragment into this element
9071          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9072          * @param {String} html The HTML fragment
9073          * @param {Boolean} returnEl True to return an Roo.Element
9074          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9075          */
9076         insertHtml : function(where, html, returnEl){
9077             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9078             return returnEl ? Roo.get(el) : el;
9079         },
9080
9081         /**
9082          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9083          * @param {Object} o The object with the attributes
9084          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9085          * @return {Roo.Element} this
9086          */
9087         set : function(o, useSet){
9088             var el = this.dom;
9089             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9090             for(var attr in o){
9091                 if(attr == "style" || typeof o[attr] == "function") continue;
9092                 if(attr=="cls"){
9093                     el.className = o["cls"];
9094                 }else{
9095                     if(useSet) el.setAttribute(attr, o[attr]);
9096                     else el[attr] = o[attr];
9097                 }
9098             }
9099             if(o.style){
9100                 Roo.DomHelper.applyStyles(el, o.style);
9101             }
9102             return this;
9103         },
9104
9105         /**
9106          * Convenience method for constructing a KeyMap
9107          * @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:
9108          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9109          * @param {Function} fn The function to call
9110          * @param {Object} scope (optional) The scope of the function
9111          * @return {Roo.KeyMap} The KeyMap created
9112          */
9113         addKeyListener : function(key, fn, scope){
9114             var config;
9115             if(typeof key != "object" || key instanceof Array){
9116                 config = {
9117                     key: key,
9118                     fn: fn,
9119                     scope: scope
9120                 };
9121             }else{
9122                 config = {
9123                     key : key.key,
9124                     shift : key.shift,
9125                     ctrl : key.ctrl,
9126                     alt : key.alt,
9127                     fn: fn,
9128                     scope: scope
9129                 };
9130             }
9131             return new Roo.KeyMap(this, config);
9132         },
9133
9134         /**
9135          * Creates a KeyMap for this element
9136          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9137          * @return {Roo.KeyMap} The KeyMap created
9138          */
9139         addKeyMap : function(config){
9140             return new Roo.KeyMap(this, config);
9141         },
9142
9143         /**
9144          * Returns true if this element is scrollable.
9145          * @return {Boolean}
9146          */
9147          isScrollable : function(){
9148             var dom = this.dom;
9149             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9150         },
9151
9152         /**
9153          * 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().
9154          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9155          * @param {Number} value The new scroll value
9156          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9157          * @return {Element} this
9158          */
9159
9160         scrollTo : function(side, value, animate){
9161             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9162             if(!animate || !A){
9163                 this.dom[prop] = value;
9164             }else{
9165                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9166                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9167             }
9168             return this;
9169         },
9170
9171         /**
9172          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9173          * within this element's scrollable range.
9174          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9175          * @param {Number} distance How far to scroll the element in pixels
9176          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9177          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9178          * was scrolled as far as it could go.
9179          */
9180          scroll : function(direction, distance, animate){
9181              if(!this.isScrollable()){
9182                  return;
9183              }
9184              var el = this.dom;
9185              var l = el.scrollLeft, t = el.scrollTop;
9186              var w = el.scrollWidth, h = el.scrollHeight;
9187              var cw = el.clientWidth, ch = el.clientHeight;
9188              direction = direction.toLowerCase();
9189              var scrolled = false;
9190              var a = this.preanim(arguments, 2);
9191              switch(direction){
9192                  case "l":
9193                  case "left":
9194                      if(w - l > cw){
9195                          var v = Math.min(l + distance, w-cw);
9196                          this.scrollTo("left", v, a);
9197                          scrolled = true;
9198                      }
9199                      break;
9200                 case "r":
9201                 case "right":
9202                      if(l > 0){
9203                          var v = Math.max(l - distance, 0);
9204                          this.scrollTo("left", v, a);
9205                          scrolled = true;
9206                      }
9207                      break;
9208                 case "t":
9209                 case "top":
9210                 case "up":
9211                      if(t > 0){
9212                          var v = Math.max(t - distance, 0);
9213                          this.scrollTo("top", v, a);
9214                          scrolled = true;
9215                      }
9216                      break;
9217                 case "b":
9218                 case "bottom":
9219                 case "down":
9220                      if(h - t > ch){
9221                          var v = Math.min(t + distance, h-ch);
9222                          this.scrollTo("top", v, a);
9223                          scrolled = true;
9224                      }
9225                      break;
9226              }
9227              return scrolled;
9228         },
9229
9230         /**
9231          * Translates the passed page coordinates into left/top css values for this element
9232          * @param {Number/Array} x The page x or an array containing [x, y]
9233          * @param {Number} y The page y
9234          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9235          */
9236         translatePoints : function(x, y){
9237             if(typeof x == 'object' || x instanceof Array){
9238                 y = x[1]; x = x[0];
9239             }
9240             var p = this.getStyle('position');
9241             var o = this.getXY();
9242
9243             var l = parseInt(this.getStyle('left'), 10);
9244             var t = parseInt(this.getStyle('top'), 10);
9245
9246             if(isNaN(l)){
9247                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9248             }
9249             if(isNaN(t)){
9250                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9251             }
9252
9253             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9254         },
9255
9256         /**
9257          * Returns the current scroll position of the element.
9258          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9259          */
9260         getScroll : function(){
9261             var d = this.dom, doc = document;
9262             if(d == doc || d == doc.body){
9263                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9264                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9265                 return {left: l, top: t};
9266             }else{
9267                 return {left: d.scrollLeft, top: d.scrollTop};
9268             }
9269         },
9270
9271         /**
9272          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9273          * are convert to standard 6 digit hex color.
9274          * @param {String} attr The css attribute
9275          * @param {String} defaultValue The default value to use when a valid color isn't found
9276          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9277          * YUI color anims.
9278          */
9279         getColor : function(attr, defaultValue, prefix){
9280             var v = this.getStyle(attr);
9281             if(!v || v == "transparent" || v == "inherit") {
9282                 return defaultValue;
9283             }
9284             var color = typeof prefix == "undefined" ? "#" : prefix;
9285             if(v.substr(0, 4) == "rgb("){
9286                 var rvs = v.slice(4, v.length -1).split(",");
9287                 for(var i = 0; i < 3; i++){
9288                     var h = parseInt(rvs[i]).toString(16);
9289                     if(h < 16){
9290                         h = "0" + h;
9291                     }
9292                     color += h;
9293                 }
9294             } else {
9295                 if(v.substr(0, 1) == "#"){
9296                     if(v.length == 4) {
9297                         for(var i = 1; i < 4; i++){
9298                             var c = v.charAt(i);
9299                             color +=  c + c;
9300                         }
9301                     }else if(v.length == 7){
9302                         color += v.substr(1);
9303                     }
9304                 }
9305             }
9306             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9307         },
9308
9309         /**
9310          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9311          * gradient background, rounded corners and a 4-way shadow.
9312          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9313          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9314          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9315          * @return {Roo.Element} this
9316          */
9317         boxWrap : function(cls){
9318             cls = cls || 'x-box';
9319             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9320             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9321             return el;
9322         },
9323
9324         /**
9325          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9326          * @param {String} namespace The namespace in which to look for the attribute
9327          * @param {String} name The attribute name
9328          * @return {String} The attribute value
9329          */
9330         getAttributeNS : Roo.isIE ? function(ns, name){
9331             var d = this.dom;
9332             var type = typeof d[ns+":"+name];
9333             if(type != 'undefined' && type != 'unknown'){
9334                 return d[ns+":"+name];
9335             }
9336             return d[name];
9337         } : function(ns, name){
9338             var d = this.dom;
9339             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9340         }
9341     };
9342
9343     var ep = El.prototype;
9344
9345     /**
9346      * Appends an event handler (Shorthand for addListener)
9347      * @param {String}   eventName     The type of event to append
9348      * @param {Function} fn        The method the event invokes
9349      * @param {Object} scope       (optional) The scope (this object) of the fn
9350      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9351      * @method
9352      */
9353     ep.on = ep.addListener;
9354         // backwards compat
9355     ep.mon = ep.addListener;
9356
9357     /**
9358      * Removes an event handler from this element (shorthand for removeListener)
9359      * @param {String} eventName the type of event to remove
9360      * @param {Function} fn the method the event invokes
9361      * @return {Roo.Element} this
9362      * @method
9363      */
9364     ep.un = ep.removeListener;
9365
9366     /**
9367      * true to automatically adjust width and height settings for box-model issues (default to true)
9368      */
9369     ep.autoBoxAdjust = true;
9370
9371     // private
9372     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9373
9374     // private
9375     El.addUnits = function(v, defaultUnit){
9376         if(v === "" || v == "auto"){
9377             return v;
9378         }
9379         if(v === undefined){
9380             return '';
9381         }
9382         if(typeof v == "number" || !El.unitPattern.test(v)){
9383             return v + (defaultUnit || 'px');
9384         }
9385         return v;
9386     };
9387
9388     // special markup used throughout Roo when box wrapping elements
9389     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>';
9390     /**
9391      * Visibility mode constant - Use visibility to hide element
9392      * @static
9393      * @type Number
9394      */
9395     El.VISIBILITY = 1;
9396     /**
9397      * Visibility mode constant - Use display to hide element
9398      * @static
9399      * @type Number
9400      */
9401     El.DISPLAY = 2;
9402
9403     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9404     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9405     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9406
9407
9408
9409     /**
9410      * @private
9411      */
9412     El.cache = {};
9413
9414     var docEl;
9415
9416     /**
9417      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9418      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9419      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9420      * @return {Element} The Element object
9421      * @static
9422      */
9423     El.get = function(el){
9424         var ex, elm, id;
9425         if(!el){ return null; }
9426         if(typeof el == "string"){ // element id
9427             if(!(elm = document.getElementById(el))){
9428                 return null;
9429             }
9430             if(ex = El.cache[el]){
9431                 ex.dom = elm;
9432             }else{
9433                 ex = El.cache[el] = new El(elm);
9434             }
9435             return ex;
9436         }else if(el.tagName){ // dom element
9437             if(!(id = el.id)){
9438                 id = Roo.id(el);
9439             }
9440             if(ex = El.cache[id]){
9441                 ex.dom = el;
9442             }else{
9443                 ex = El.cache[id] = new El(el);
9444             }
9445             return ex;
9446         }else if(el instanceof El){
9447             if(el != docEl){
9448                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9449                                                               // catch case where it hasn't been appended
9450                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9451             }
9452             return el;
9453         }else if(el.isComposite){
9454             return el;
9455         }else if(el instanceof Array){
9456             return El.select(el);
9457         }else if(el == document){
9458             // create a bogus element object representing the document object
9459             if(!docEl){
9460                 var f = function(){};
9461                 f.prototype = El.prototype;
9462                 docEl = new f();
9463                 docEl.dom = document;
9464             }
9465             return docEl;
9466         }
9467         return null;
9468     };
9469
9470     // private
9471     El.uncache = function(el){
9472         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9473             if(a[i]){
9474                 delete El.cache[a[i].id || a[i]];
9475             }
9476         }
9477     };
9478
9479     // private
9480     // Garbage collection - uncache elements/purge listeners on orphaned elements
9481     // so we don't hold a reference and cause the browser to retain them
9482     El.garbageCollect = function(){
9483         if(!Roo.enableGarbageCollector){
9484             clearInterval(El.collectorThread);
9485             return;
9486         }
9487         for(var eid in El.cache){
9488             var el = El.cache[eid], d = el.dom;
9489             // -------------------------------------------------------
9490             // Determining what is garbage:
9491             // -------------------------------------------------------
9492             // !d
9493             // dom node is null, definitely garbage
9494             // -------------------------------------------------------
9495             // !d.parentNode
9496             // no parentNode == direct orphan, definitely garbage
9497             // -------------------------------------------------------
9498             // !d.offsetParent && !document.getElementById(eid)
9499             // display none elements have no offsetParent so we will
9500             // also try to look it up by it's id. However, check
9501             // offsetParent first so we don't do unneeded lookups.
9502             // This enables collection of elements that are not orphans
9503             // directly, but somewhere up the line they have an orphan
9504             // parent.
9505             // -------------------------------------------------------
9506             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9507                 delete El.cache[eid];
9508                 if(d && Roo.enableListenerCollection){
9509                     E.purgeElement(d);
9510                 }
9511             }
9512         }
9513     }
9514     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9515
9516
9517     // dom is optional
9518     El.Flyweight = function(dom){
9519         this.dom = dom;
9520     };
9521     El.Flyweight.prototype = El.prototype;
9522
9523     El._flyweights = {};
9524     /**
9525      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9526      * the dom node can be overwritten by other code.
9527      * @param {String/HTMLElement} el The dom node or id
9528      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9529      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9530      * @static
9531      * @return {Element} The shared Element object
9532      */
9533     El.fly = function(el, named){
9534         named = named || '_global';
9535         el = Roo.getDom(el);
9536         if(!el){
9537             return null;
9538         }
9539         if(!El._flyweights[named]){
9540             El._flyweights[named] = new El.Flyweight();
9541         }
9542         El._flyweights[named].dom = el;
9543         return El._flyweights[named];
9544     };
9545
9546     /**
9547      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9548      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9549      * Shorthand of {@link Roo.Element#get}
9550      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9551      * @return {Element} The Element object
9552      * @member Roo
9553      * @method get
9554      */
9555     Roo.get = El.get;
9556     /**
9557      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9558      * the dom node can be overwritten by other code.
9559      * Shorthand of {@link Roo.Element#fly}
9560      * @param {String/HTMLElement} el The dom node or id
9561      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9562      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9563      * @static
9564      * @return {Element} The shared Element object
9565      * @member Roo
9566      * @method fly
9567      */
9568     Roo.fly = El.fly;
9569
9570     // speedy lookup for elements never to box adjust
9571     var noBoxAdjust = Roo.isStrict ? {
9572         select:1
9573     } : {
9574         input:1, select:1, textarea:1
9575     };
9576     if(Roo.isIE || Roo.isGecko){
9577         noBoxAdjust['button'] = 1;
9578     }
9579
9580
9581     Roo.EventManager.on(window, 'unload', function(){
9582         delete El.cache;
9583         delete El._flyweights;
9584     });
9585 })();
9586
9587
9588
9589
9590 if(Roo.DomQuery){
9591     Roo.Element.selectorFunction = Roo.DomQuery.select;
9592 }
9593
9594 Roo.Element.select = function(selector, unique, root){
9595     var els;
9596     if(typeof selector == "string"){
9597         els = Roo.Element.selectorFunction(selector, root);
9598     }else if(selector.length !== undefined){
9599         els = selector;
9600     }else{
9601         throw "Invalid selector";
9602     }
9603     if(unique === true){
9604         return new Roo.CompositeElement(els);
9605     }else{
9606         return new Roo.CompositeElementLite(els);
9607     }
9608 };
9609 /**
9610  * Selects elements based on the passed CSS selector to enable working on them as 1.
9611  * @param {String/Array} selector The CSS selector or an array of elements
9612  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9613  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9614  * @return {CompositeElementLite/CompositeElement}
9615  * @member Roo
9616  * @method select
9617  */
9618 Roo.select = Roo.Element.select;
9619
9620
9621
9622
9623
9624
9625
9626
9627
9628
9629
9630
9631
9632
9633 /*
9634  * Based on:
9635  * Ext JS Library 1.1.1
9636  * Copyright(c) 2006-2007, Ext JS, LLC.
9637  *
9638  * Originally Released Under LGPL - original licence link has changed is not relivant.
9639  *
9640  * Fork - LGPL
9641  * <script type="text/javascript">
9642  */
9643
9644
9645
9646 //Notifies Element that fx methods are available
9647 Roo.enableFx = true;
9648
9649 /**
9650  * @class Roo.Fx
9651  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9652  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9653  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9654  * Element effects to work.</p><br/>
9655  *
9656  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9657  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9658  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9659  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9660  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9661  * expected results and should be done with care.</p><br/>
9662  *
9663  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9664  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9665 <pre>
9666 Value  Description
9667 -----  -----------------------------
9668 tl     The top left corner
9669 t      The center of the top edge
9670 tr     The top right corner
9671 l      The center of the left edge
9672 r      The center of the right edge
9673 bl     The bottom left corner
9674 b      The center of the bottom edge
9675 br     The bottom right corner
9676 </pre>
9677  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9678  * below are common options that can be passed to any Fx method.</b>
9679  * @cfg {Function} callback A function called when the effect is finished
9680  * @cfg {Object} scope The scope of the effect function
9681  * @cfg {String} easing A valid Easing value for the effect
9682  * @cfg {String} afterCls A css class to apply after the effect
9683  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9684  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9685  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9686  * effects that end with the element being visually hidden, ignored otherwise)
9687  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9688  * a function which returns such a specification that will be applied to the Element after the effect finishes
9689  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9690  * @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
9691  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9692  */
9693 Roo.Fx = {
9694         /**
9695          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9696          * origin for the slide effect.  This function automatically handles wrapping the element with
9697          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9698          * Usage:
9699          *<pre><code>
9700 // default: slide the element in from the top
9701 el.slideIn();
9702
9703 // custom: slide the element in from the right with a 2-second duration
9704 el.slideIn('r', { duration: 2 });
9705
9706 // common config options shown with default values
9707 el.slideIn('t', {
9708     easing: 'easeOut',
9709     duration: .5
9710 });
9711 </code></pre>
9712          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9713          * @param {Object} options (optional) Object literal with any of the Fx config options
9714          * @return {Roo.Element} The Element
9715          */
9716     slideIn : function(anchor, o){
9717         var el = this.getFxEl();
9718         o = o || {};
9719
9720         el.queueFx(o, function(){
9721
9722             anchor = anchor || "t";
9723
9724             // fix display to visibility
9725             this.fixDisplay();
9726
9727             // restore values after effect
9728             var r = this.getFxRestore();
9729             var b = this.getBox();
9730             // fixed size for slide
9731             this.setSize(b);
9732
9733             // wrap if needed
9734             var wrap = this.fxWrap(r.pos, o, "hidden");
9735
9736             var st = this.dom.style;
9737             st.visibility = "visible";
9738             st.position = "absolute";
9739
9740             // clear out temp styles after slide and unwrap
9741             var after = function(){
9742                 el.fxUnwrap(wrap, r.pos, o);
9743                 st.width = r.width;
9744                 st.height = r.height;
9745                 el.afterFx(o);
9746             };
9747             // time to calc the positions
9748             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9749
9750             switch(anchor.toLowerCase()){
9751                 case "t":
9752                     wrap.setSize(b.width, 0);
9753                     st.left = st.bottom = "0";
9754                     a = {height: bh};
9755                 break;
9756                 case "l":
9757                     wrap.setSize(0, b.height);
9758                     st.right = st.top = "0";
9759                     a = {width: bw};
9760                 break;
9761                 case "r":
9762                     wrap.setSize(0, b.height);
9763                     wrap.setX(b.right);
9764                     st.left = st.top = "0";
9765                     a = {width: bw, points: pt};
9766                 break;
9767                 case "b":
9768                     wrap.setSize(b.width, 0);
9769                     wrap.setY(b.bottom);
9770                     st.left = st.top = "0";
9771                     a = {height: bh, points: pt};
9772                 break;
9773                 case "tl":
9774                     wrap.setSize(0, 0);
9775                     st.right = st.bottom = "0";
9776                     a = {width: bw, height: bh};
9777                 break;
9778                 case "bl":
9779                     wrap.setSize(0, 0);
9780                     wrap.setY(b.y+b.height);
9781                     st.right = st.top = "0";
9782                     a = {width: bw, height: bh, points: pt};
9783                 break;
9784                 case "br":
9785                     wrap.setSize(0, 0);
9786                     wrap.setXY([b.right, b.bottom]);
9787                     st.left = st.top = "0";
9788                     a = {width: bw, height: bh, points: pt};
9789                 break;
9790                 case "tr":
9791                     wrap.setSize(0, 0);
9792                     wrap.setX(b.x+b.width);
9793                     st.left = st.bottom = "0";
9794                     a = {width: bw, height: bh, points: pt};
9795                 break;
9796             }
9797             this.dom.style.visibility = "visible";
9798             wrap.show();
9799
9800             arguments.callee.anim = wrap.fxanim(a,
9801                 o,
9802                 'motion',
9803                 .5,
9804                 'easeOut', after);
9805         });
9806         return this;
9807     },
9808     
9809         /**
9810          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9811          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9812          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9813          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9814          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9815          * Usage:
9816          *<pre><code>
9817 // default: slide the element out to the top
9818 el.slideOut();
9819
9820 // custom: slide the element out to the right with a 2-second duration
9821 el.slideOut('r', { duration: 2 });
9822
9823 // common config options shown with default values
9824 el.slideOut('t', {
9825     easing: 'easeOut',
9826     duration: .5,
9827     remove: false,
9828     useDisplay: false
9829 });
9830 </code></pre>
9831          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9832          * @param {Object} options (optional) Object literal with any of the Fx config options
9833          * @return {Roo.Element} The Element
9834          */
9835     slideOut : function(anchor, o){
9836         var el = this.getFxEl();
9837         o = o || {};
9838
9839         el.queueFx(o, function(){
9840
9841             anchor = anchor || "t";
9842
9843             // restore values after effect
9844             var r = this.getFxRestore();
9845             
9846             var b = this.getBox();
9847             // fixed size for slide
9848             this.setSize(b);
9849
9850             // wrap if needed
9851             var wrap = this.fxWrap(r.pos, o, "visible");
9852
9853             var st = this.dom.style;
9854             st.visibility = "visible";
9855             st.position = "absolute";
9856
9857             wrap.setSize(b);
9858
9859             var after = function(){
9860                 if(o.useDisplay){
9861                     el.setDisplayed(false);
9862                 }else{
9863                     el.hide();
9864                 }
9865
9866                 el.fxUnwrap(wrap, r.pos, o);
9867
9868                 st.width = r.width;
9869                 st.height = r.height;
9870
9871                 el.afterFx(o);
9872             };
9873
9874             var a, zero = {to: 0};
9875             switch(anchor.toLowerCase()){
9876                 case "t":
9877                     st.left = st.bottom = "0";
9878                     a = {height: zero};
9879                 break;
9880                 case "l":
9881                     st.right = st.top = "0";
9882                     a = {width: zero};
9883                 break;
9884                 case "r":
9885                     st.left = st.top = "0";
9886                     a = {width: zero, points: {to:[b.right, b.y]}};
9887                 break;
9888                 case "b":
9889                     st.left = st.top = "0";
9890                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9891                 break;
9892                 case "tl":
9893                     st.right = st.bottom = "0";
9894                     a = {width: zero, height: zero};
9895                 break;
9896                 case "bl":
9897                     st.right = st.top = "0";
9898                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9899                 break;
9900                 case "br":
9901                     st.left = st.top = "0";
9902                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9903                 break;
9904                 case "tr":
9905                     st.left = st.bottom = "0";
9906                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9907                 break;
9908             }
9909
9910             arguments.callee.anim = wrap.fxanim(a,
9911                 o,
9912                 'motion',
9913                 .5,
9914                 "easeOut", after);
9915         });
9916         return this;
9917     },
9918
9919         /**
9920          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9921          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9922          * The element must be removed from the DOM using the 'remove' config option if desired.
9923          * Usage:
9924          *<pre><code>
9925 // default
9926 el.puff();
9927
9928 // common config options shown with default values
9929 el.puff({
9930     easing: 'easeOut',
9931     duration: .5,
9932     remove: false,
9933     useDisplay: false
9934 });
9935 </code></pre>
9936          * @param {Object} options (optional) Object literal with any of the Fx config options
9937          * @return {Roo.Element} The Element
9938          */
9939     puff : function(o){
9940         var el = this.getFxEl();
9941         o = o || {};
9942
9943         el.queueFx(o, function(){
9944             this.clearOpacity();
9945             this.show();
9946
9947             // restore values after effect
9948             var r = this.getFxRestore();
9949             var st = this.dom.style;
9950
9951             var after = function(){
9952                 if(o.useDisplay){
9953                     el.setDisplayed(false);
9954                 }else{
9955                     el.hide();
9956                 }
9957
9958                 el.clearOpacity();
9959
9960                 el.setPositioning(r.pos);
9961                 st.width = r.width;
9962                 st.height = r.height;
9963                 st.fontSize = '';
9964                 el.afterFx(o);
9965             };
9966
9967             var width = this.getWidth();
9968             var height = this.getHeight();
9969
9970             arguments.callee.anim = this.fxanim({
9971                     width : {to: this.adjustWidth(width * 2)},
9972                     height : {to: this.adjustHeight(height * 2)},
9973                     points : {by: [-(width * .5), -(height * .5)]},
9974                     opacity : {to: 0},
9975                     fontSize: {to:200, unit: "%"}
9976                 },
9977                 o,
9978                 'motion',
9979                 .5,
9980                 "easeOut", after);
9981         });
9982         return this;
9983     },
9984
9985         /**
9986          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9987          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9988          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9989          * Usage:
9990          *<pre><code>
9991 // default
9992 el.switchOff();
9993
9994 // all config options shown with default values
9995 el.switchOff({
9996     easing: 'easeIn',
9997     duration: .3,
9998     remove: false,
9999     useDisplay: false
10000 });
10001 </code></pre>
10002          * @param {Object} options (optional) Object literal with any of the Fx config options
10003          * @return {Roo.Element} The Element
10004          */
10005     switchOff : function(o){
10006         var el = this.getFxEl();
10007         o = o || {};
10008
10009         el.queueFx(o, function(){
10010             this.clearOpacity();
10011             this.clip();
10012
10013             // restore values after effect
10014             var r = this.getFxRestore();
10015             var st = this.dom.style;
10016
10017             var after = function(){
10018                 if(o.useDisplay){
10019                     el.setDisplayed(false);
10020                 }else{
10021                     el.hide();
10022                 }
10023
10024                 el.clearOpacity();
10025                 el.setPositioning(r.pos);
10026                 st.width = r.width;
10027                 st.height = r.height;
10028
10029                 el.afterFx(o);
10030             };
10031
10032             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10033                 this.clearOpacity();
10034                 (function(){
10035                     this.fxanim({
10036                         height:{to:1},
10037                         points:{by:[0, this.getHeight() * .5]}
10038                     }, o, 'motion', 0.3, 'easeIn', after);
10039                 }).defer(100, this);
10040             });
10041         });
10042         return this;
10043     },
10044
10045     /**
10046      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10047      * changed using the "attr" config option) and then fading back to the original color. If no original
10048      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10049      * Usage:
10050 <pre><code>
10051 // default: highlight background to yellow
10052 el.highlight();
10053
10054 // custom: highlight foreground text to blue for 2 seconds
10055 el.highlight("0000ff", { attr: 'color', duration: 2 });
10056
10057 // common config options shown with default values
10058 el.highlight("ffff9c", {
10059     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10060     endColor: (current color) or "ffffff",
10061     easing: 'easeIn',
10062     duration: 1
10063 });
10064 </code></pre>
10065      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10066      * @param {Object} options (optional) Object literal with any of the Fx config options
10067      * @return {Roo.Element} The Element
10068      */ 
10069     highlight : function(color, o){
10070         var el = this.getFxEl();
10071         o = o || {};
10072
10073         el.queueFx(o, function(){
10074             color = color || "ffff9c";
10075             attr = o.attr || "backgroundColor";
10076
10077             this.clearOpacity();
10078             this.show();
10079
10080             var origColor = this.getColor(attr);
10081             var restoreColor = this.dom.style[attr];
10082             endColor = (o.endColor || origColor) || "ffffff";
10083
10084             var after = function(){
10085                 el.dom.style[attr] = restoreColor;
10086                 el.afterFx(o);
10087             };
10088
10089             var a = {};
10090             a[attr] = {from: color, to: endColor};
10091             arguments.callee.anim = this.fxanim(a,
10092                 o,
10093                 'color',
10094                 1,
10095                 'easeIn', after);
10096         });
10097         return this;
10098     },
10099
10100    /**
10101     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10102     * Usage:
10103 <pre><code>
10104 // default: a single light blue ripple
10105 el.frame();
10106
10107 // custom: 3 red ripples lasting 3 seconds total
10108 el.frame("ff0000", 3, { duration: 3 });
10109
10110 // common config options shown with default values
10111 el.frame("C3DAF9", 1, {
10112     duration: 1 //duration of entire animation (not each individual ripple)
10113     // Note: Easing is not configurable and will be ignored if included
10114 });
10115 </code></pre>
10116     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10117     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10118     * @param {Object} options (optional) Object literal with any of the Fx config options
10119     * @return {Roo.Element} The Element
10120     */
10121     frame : function(color, count, o){
10122         var el = this.getFxEl();
10123         o = o || {};
10124
10125         el.queueFx(o, function(){
10126             color = color || "#C3DAF9";
10127             if(color.length == 6){
10128                 color = "#" + color;
10129             }
10130             count = count || 1;
10131             duration = o.duration || 1;
10132             this.show();
10133
10134             var b = this.getBox();
10135             var animFn = function(){
10136                 var proxy = this.createProxy({
10137
10138                      style:{
10139                         visbility:"hidden",
10140                         position:"absolute",
10141                         "z-index":"35000", // yee haw
10142                         border:"0px solid " + color
10143                      }
10144                   });
10145                 var scale = Roo.isBorderBox ? 2 : 1;
10146                 proxy.animate({
10147                     top:{from:b.y, to:b.y - 20},
10148                     left:{from:b.x, to:b.x - 20},
10149                     borderWidth:{from:0, to:10},
10150                     opacity:{from:1, to:0},
10151                     height:{from:b.height, to:(b.height + (20*scale))},
10152                     width:{from:b.width, to:(b.width + (20*scale))}
10153                 }, duration, function(){
10154                     proxy.remove();
10155                 });
10156                 if(--count > 0){
10157                      animFn.defer((duration/2)*1000, this);
10158                 }else{
10159                     el.afterFx(o);
10160                 }
10161             };
10162             animFn.call(this);
10163         });
10164         return this;
10165     },
10166
10167    /**
10168     * Creates a pause before any subsequent queued effects begin.  If there are
10169     * no effects queued after the pause it will have no effect.
10170     * Usage:
10171 <pre><code>
10172 el.pause(1);
10173 </code></pre>
10174     * @param {Number} seconds The length of time to pause (in seconds)
10175     * @return {Roo.Element} The Element
10176     */
10177     pause : function(seconds){
10178         var el = this.getFxEl();
10179         var o = {};
10180
10181         el.queueFx(o, function(){
10182             setTimeout(function(){
10183                 el.afterFx(o);
10184             }, seconds * 1000);
10185         });
10186         return this;
10187     },
10188
10189    /**
10190     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10191     * using the "endOpacity" config option.
10192     * Usage:
10193 <pre><code>
10194 // default: fade in from opacity 0 to 100%
10195 el.fadeIn();
10196
10197 // custom: fade in from opacity 0 to 75% over 2 seconds
10198 el.fadeIn({ endOpacity: .75, duration: 2});
10199
10200 // common config options shown with default values
10201 el.fadeIn({
10202     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10203     easing: 'easeOut',
10204     duration: .5
10205 });
10206 </code></pre>
10207     * @param {Object} options (optional) Object literal with any of the Fx config options
10208     * @return {Roo.Element} The Element
10209     */
10210     fadeIn : function(o){
10211         var el = this.getFxEl();
10212         o = o || {};
10213         el.queueFx(o, function(){
10214             this.setOpacity(0);
10215             this.fixDisplay();
10216             this.dom.style.visibility = 'visible';
10217             var to = o.endOpacity || 1;
10218             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10219                 o, null, .5, "easeOut", function(){
10220                 if(to == 1){
10221                     this.clearOpacity();
10222                 }
10223                 el.afterFx(o);
10224             });
10225         });
10226         return this;
10227     },
10228
10229    /**
10230     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10231     * using the "endOpacity" config option.
10232     * Usage:
10233 <pre><code>
10234 // default: fade out from the element's current opacity to 0
10235 el.fadeOut();
10236
10237 // custom: fade out from the element's current opacity to 25% over 2 seconds
10238 el.fadeOut({ endOpacity: .25, duration: 2});
10239
10240 // common config options shown with default values
10241 el.fadeOut({
10242     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10243     easing: 'easeOut',
10244     duration: .5
10245     remove: false,
10246     useDisplay: false
10247 });
10248 </code></pre>
10249     * @param {Object} options (optional) Object literal with any of the Fx config options
10250     * @return {Roo.Element} The Element
10251     */
10252     fadeOut : function(o){
10253         var el = this.getFxEl();
10254         o = o || {};
10255         el.queueFx(o, function(){
10256             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10257                 o, null, .5, "easeOut", function(){
10258                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10259                      this.dom.style.display = "none";
10260                 }else{
10261                      this.dom.style.visibility = "hidden";
10262                 }
10263                 this.clearOpacity();
10264                 el.afterFx(o);
10265             });
10266         });
10267         return this;
10268     },
10269
10270    /**
10271     * Animates the transition of an element's dimensions from a starting height/width
10272     * to an ending height/width.
10273     * Usage:
10274 <pre><code>
10275 // change height and width to 100x100 pixels
10276 el.scale(100, 100);
10277
10278 // common config options shown with default values.  The height and width will default to
10279 // the element's existing values if passed as null.
10280 el.scale(
10281     [element's width],
10282     [element's height], {
10283     easing: 'easeOut',
10284     duration: .35
10285 });
10286 </code></pre>
10287     * @param {Number} width  The new width (pass undefined to keep the original width)
10288     * @param {Number} height  The new height (pass undefined to keep the original height)
10289     * @param {Object} options (optional) Object literal with any of the Fx config options
10290     * @return {Roo.Element} The Element
10291     */
10292     scale : function(w, h, o){
10293         this.shift(Roo.apply({}, o, {
10294             width: w,
10295             height: h
10296         }));
10297         return this;
10298     },
10299
10300    /**
10301     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10302     * Any of these properties not specified in the config object will not be changed.  This effect 
10303     * requires that at least one new dimension, position or opacity setting must be passed in on
10304     * the config object in order for the function to have any effect.
10305     * Usage:
10306 <pre><code>
10307 // slide the element horizontally to x position 200 while changing the height and opacity
10308 el.shift({ x: 200, height: 50, opacity: .8 });
10309
10310 // common config options shown with default values.
10311 el.shift({
10312     width: [element's width],
10313     height: [element's height],
10314     x: [element's x position],
10315     y: [element's y position],
10316     opacity: [element's opacity],
10317     easing: 'easeOut',
10318     duration: .35
10319 });
10320 </code></pre>
10321     * @param {Object} options  Object literal with any of the Fx config options
10322     * @return {Roo.Element} The Element
10323     */
10324     shift : function(o){
10325         var el = this.getFxEl();
10326         o = o || {};
10327         el.queueFx(o, function(){
10328             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10329             if(w !== undefined){
10330                 a.width = {to: this.adjustWidth(w)};
10331             }
10332             if(h !== undefined){
10333                 a.height = {to: this.adjustHeight(h)};
10334             }
10335             if(x !== undefined || y !== undefined){
10336                 a.points = {to: [
10337                     x !== undefined ? x : this.getX(),
10338                     y !== undefined ? y : this.getY()
10339                 ]};
10340             }
10341             if(op !== undefined){
10342                 a.opacity = {to: op};
10343             }
10344             if(o.xy !== undefined){
10345                 a.points = {to: o.xy};
10346             }
10347             arguments.callee.anim = this.fxanim(a,
10348                 o, 'motion', .35, "easeOut", function(){
10349                 el.afterFx(o);
10350             });
10351         });
10352         return this;
10353     },
10354
10355         /**
10356          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10357          * ending point of the effect.
10358          * Usage:
10359          *<pre><code>
10360 // default: slide the element downward while fading out
10361 el.ghost();
10362
10363 // custom: slide the element out to the right with a 2-second duration
10364 el.ghost('r', { duration: 2 });
10365
10366 // common config options shown with default values
10367 el.ghost('b', {
10368     easing: 'easeOut',
10369     duration: .5
10370     remove: false,
10371     useDisplay: false
10372 });
10373 </code></pre>
10374          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10375          * @param {Object} options (optional) Object literal with any of the Fx config options
10376          * @return {Roo.Element} The Element
10377          */
10378     ghost : function(anchor, o){
10379         var el = this.getFxEl();
10380         o = o || {};
10381
10382         el.queueFx(o, function(){
10383             anchor = anchor || "b";
10384
10385             // restore values after effect
10386             var r = this.getFxRestore();
10387             var w = this.getWidth(),
10388                 h = this.getHeight();
10389
10390             var st = this.dom.style;
10391
10392             var after = function(){
10393                 if(o.useDisplay){
10394                     el.setDisplayed(false);
10395                 }else{
10396                     el.hide();
10397                 }
10398
10399                 el.clearOpacity();
10400                 el.setPositioning(r.pos);
10401                 st.width = r.width;
10402                 st.height = r.height;
10403
10404                 el.afterFx(o);
10405             };
10406
10407             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10408             switch(anchor.toLowerCase()){
10409                 case "t":
10410                     pt.by = [0, -h];
10411                 break;
10412                 case "l":
10413                     pt.by = [-w, 0];
10414                 break;
10415                 case "r":
10416                     pt.by = [w, 0];
10417                 break;
10418                 case "b":
10419                     pt.by = [0, h];
10420                 break;
10421                 case "tl":
10422                     pt.by = [-w, -h];
10423                 break;
10424                 case "bl":
10425                     pt.by = [-w, h];
10426                 break;
10427                 case "br":
10428                     pt.by = [w, h];
10429                 break;
10430                 case "tr":
10431                     pt.by = [w, -h];
10432                 break;
10433             }
10434
10435             arguments.callee.anim = this.fxanim(a,
10436                 o,
10437                 'motion',
10438                 .5,
10439                 "easeOut", after);
10440         });
10441         return this;
10442     },
10443
10444         /**
10445          * Ensures that all effects queued after syncFx is called on the element are
10446          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10447          * @return {Roo.Element} The Element
10448          */
10449     syncFx : function(){
10450         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10451             block : false,
10452             concurrent : true,
10453             stopFx : false
10454         });
10455         return this;
10456     },
10457
10458         /**
10459          * Ensures that all effects queued after sequenceFx is called on the element are
10460          * run in sequence.  This is the opposite of {@link #syncFx}.
10461          * @return {Roo.Element} The Element
10462          */
10463     sequenceFx : function(){
10464         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10465             block : false,
10466             concurrent : false,
10467             stopFx : false
10468         });
10469         return this;
10470     },
10471
10472         /* @private */
10473     nextFx : function(){
10474         var ef = this.fxQueue[0];
10475         if(ef){
10476             ef.call(this);
10477         }
10478     },
10479
10480         /**
10481          * Returns true if the element has any effects actively running or queued, else returns false.
10482          * @return {Boolean} True if element has active effects, else false
10483          */
10484     hasActiveFx : function(){
10485         return this.fxQueue && this.fxQueue[0];
10486     },
10487
10488         /**
10489          * Stops any running effects and clears the element's internal effects queue if it contains
10490          * any additional effects that haven't started yet.
10491          * @return {Roo.Element} The Element
10492          */
10493     stopFx : function(){
10494         if(this.hasActiveFx()){
10495             var cur = this.fxQueue[0];
10496             if(cur && cur.anim && cur.anim.isAnimated()){
10497                 this.fxQueue = [cur]; // clear out others
10498                 cur.anim.stop(true);
10499             }
10500         }
10501         return this;
10502     },
10503
10504         /* @private */
10505     beforeFx : function(o){
10506         if(this.hasActiveFx() && !o.concurrent){
10507            if(o.stopFx){
10508                this.stopFx();
10509                return true;
10510            }
10511            return false;
10512         }
10513         return true;
10514     },
10515
10516         /**
10517          * Returns true if the element is currently blocking so that no other effect can be queued
10518          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10519          * used to ensure that an effect initiated by a user action runs to completion prior to the
10520          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10521          * @return {Boolean} True if blocking, else false
10522          */
10523     hasFxBlock : function(){
10524         var q = this.fxQueue;
10525         return q && q[0] && q[0].block;
10526     },
10527
10528         /* @private */
10529     queueFx : function(o, fn){
10530         if(!this.fxQueue){
10531             this.fxQueue = [];
10532         }
10533         if(!this.hasFxBlock()){
10534             Roo.applyIf(o, this.fxDefaults);
10535             if(!o.concurrent){
10536                 var run = this.beforeFx(o);
10537                 fn.block = o.block;
10538                 this.fxQueue.push(fn);
10539                 if(run){
10540                     this.nextFx();
10541                 }
10542             }else{
10543                 fn.call(this);
10544             }
10545         }
10546         return this;
10547     },
10548
10549         /* @private */
10550     fxWrap : function(pos, o, vis){
10551         var wrap;
10552         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10553             var wrapXY;
10554             if(o.fixPosition){
10555                 wrapXY = this.getXY();
10556             }
10557             var div = document.createElement("div");
10558             div.style.visibility = vis;
10559             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10560             wrap.setPositioning(pos);
10561             if(wrap.getStyle("position") == "static"){
10562                 wrap.position("relative");
10563             }
10564             this.clearPositioning('auto');
10565             wrap.clip();
10566             wrap.dom.appendChild(this.dom);
10567             if(wrapXY){
10568                 wrap.setXY(wrapXY);
10569             }
10570         }
10571         return wrap;
10572     },
10573
10574         /* @private */
10575     fxUnwrap : function(wrap, pos, o){
10576         this.clearPositioning();
10577         this.setPositioning(pos);
10578         if(!o.wrap){
10579             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10580             wrap.remove();
10581         }
10582     },
10583
10584         /* @private */
10585     getFxRestore : function(){
10586         var st = this.dom.style;
10587         return {pos: this.getPositioning(), width: st.width, height : st.height};
10588     },
10589
10590         /* @private */
10591     afterFx : function(o){
10592         if(o.afterStyle){
10593             this.applyStyles(o.afterStyle);
10594         }
10595         if(o.afterCls){
10596             this.addClass(o.afterCls);
10597         }
10598         if(o.remove === true){
10599             this.remove();
10600         }
10601         Roo.callback(o.callback, o.scope, [this]);
10602         if(!o.concurrent){
10603             this.fxQueue.shift();
10604             this.nextFx();
10605         }
10606     },
10607
10608         /* @private */
10609     getFxEl : function(){ // support for composite element fx
10610         return Roo.get(this.dom);
10611     },
10612
10613         /* @private */
10614     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10615         animType = animType || 'run';
10616         opt = opt || {};
10617         var anim = Roo.lib.Anim[animType](
10618             this.dom, args,
10619             (opt.duration || defaultDur) || .35,
10620             (opt.easing || defaultEase) || 'easeOut',
10621             function(){
10622                 Roo.callback(cb, this);
10623             },
10624             this
10625         );
10626         opt.anim = anim;
10627         return anim;
10628     }
10629 };
10630
10631 // backwords compat
10632 Roo.Fx.resize = Roo.Fx.scale;
10633
10634 //When included, Roo.Fx is automatically applied to Element so that all basic
10635 //effects are available directly via the Element API
10636 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10637  * Based on:
10638  * Ext JS Library 1.1.1
10639  * Copyright(c) 2006-2007, Ext JS, LLC.
10640  *
10641  * Originally Released Under LGPL - original licence link has changed is not relivant.
10642  *
10643  * Fork - LGPL
10644  * <script type="text/javascript">
10645  */
10646
10647
10648 /**
10649  * @class Roo.CompositeElement
10650  * Standard composite class. Creates a Roo.Element for every element in the collection.
10651  * <br><br>
10652  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10653  * actions will be performed on all the elements in this collection.</b>
10654  * <br><br>
10655  * All methods return <i>this</i> and can be chained.
10656  <pre><code>
10657  var els = Roo.select("#some-el div.some-class", true);
10658  // or select directly from an existing element
10659  var el = Roo.get('some-el');
10660  el.select('div.some-class', true);
10661
10662  els.setWidth(100); // all elements become 100 width
10663  els.hide(true); // all elements fade out and hide
10664  // or
10665  els.setWidth(100).hide(true);
10666  </code></pre>
10667  */
10668 Roo.CompositeElement = function(els){
10669     this.elements = [];
10670     this.addElements(els);
10671 };
10672 Roo.CompositeElement.prototype = {
10673     isComposite: true,
10674     addElements : function(els){
10675         if(!els) return this;
10676         if(typeof els == "string"){
10677             els = Roo.Element.selectorFunction(els);
10678         }
10679         var yels = this.elements;
10680         var index = yels.length-1;
10681         for(var i = 0, len = els.length; i < len; i++) {
10682                 yels[++index] = Roo.get(els[i]);
10683         }
10684         return this;
10685     },
10686
10687     /**
10688     * Clears this composite and adds the elements returned by the passed selector.
10689     * @param {String/Array} els A string CSS selector, an array of elements or an element
10690     * @return {CompositeElement} this
10691     */
10692     fill : function(els){
10693         this.elements = [];
10694         this.add(els);
10695         return this;
10696     },
10697
10698     /**
10699     * Filters this composite to only elements that match the passed selector.
10700     * @param {String} selector A string CSS selector
10701     * @return {CompositeElement} this
10702     */
10703     filter : function(selector){
10704         var els = [];
10705         this.each(function(el){
10706             if(el.is(selector)){
10707                 els[els.length] = el.dom;
10708             }
10709         });
10710         this.fill(els);
10711         return this;
10712     },
10713
10714     invoke : function(fn, args){
10715         var els = this.elements;
10716         for(var i = 0, len = els.length; i < len; i++) {
10717                 Roo.Element.prototype[fn].apply(els[i], args);
10718         }
10719         return this;
10720     },
10721     /**
10722     * Adds elements to this composite.
10723     * @param {String/Array} els A string CSS selector, an array of elements or an element
10724     * @return {CompositeElement} this
10725     */
10726     add : function(els){
10727         if(typeof els == "string"){
10728             this.addElements(Roo.Element.selectorFunction(els));
10729         }else if(els.length !== undefined){
10730             this.addElements(els);
10731         }else{
10732             this.addElements([els]);
10733         }
10734         return this;
10735     },
10736     /**
10737     * Calls the passed function passing (el, this, index) for each element in this composite.
10738     * @param {Function} fn The function to call
10739     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10740     * @return {CompositeElement} this
10741     */
10742     each : function(fn, scope){
10743         var els = this.elements;
10744         for(var i = 0, len = els.length; i < len; i++){
10745             if(fn.call(scope || els[i], els[i], this, i) === false) {
10746                 break;
10747             }
10748         }
10749         return this;
10750     },
10751
10752     /**
10753      * Returns the Element object at the specified index
10754      * @param {Number} index
10755      * @return {Roo.Element}
10756      */
10757     item : function(index){
10758         return this.elements[index] || null;
10759     },
10760
10761     /**
10762      * Returns the first Element
10763      * @return {Roo.Element}
10764      */
10765     first : function(){
10766         return this.item(0);
10767     },
10768
10769     /**
10770      * Returns the last Element
10771      * @return {Roo.Element}
10772      */
10773     last : function(){
10774         return this.item(this.elements.length-1);
10775     },
10776
10777     /**
10778      * Returns the number of elements in this composite
10779      * @return Number
10780      */
10781     getCount : function(){
10782         return this.elements.length;
10783     },
10784
10785     /**
10786      * Returns true if this composite contains the passed element
10787      * @return Boolean
10788      */
10789     contains : function(el){
10790         return this.indexOf(el) !== -1;
10791     },
10792
10793     /**
10794      * Returns true if this composite contains the passed element
10795      * @return Boolean
10796      */
10797     indexOf : function(el){
10798         return this.elements.indexOf(Roo.get(el));
10799     },
10800
10801
10802     /**
10803     * Removes the specified element(s).
10804     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10805     * or an array of any of those.
10806     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10807     * @return {CompositeElement} this
10808     */
10809     removeElement : function(el, removeDom){
10810         if(el instanceof Array){
10811             for(var i = 0, len = el.length; i < len; i++){
10812                 this.removeElement(el[i]);
10813             }
10814             return this;
10815         }
10816         var index = typeof el == 'number' ? el : this.indexOf(el);
10817         if(index !== -1){
10818             if(removeDom){
10819                 var d = this.elements[index];
10820                 if(d.dom){
10821                     d.remove();
10822                 }else{
10823                     d.parentNode.removeChild(d);
10824                 }
10825             }
10826             this.elements.splice(index, 1);
10827         }
10828         return this;
10829     },
10830
10831     /**
10832     * Replaces the specified element with the passed element.
10833     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10834     * to replace.
10835     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10836     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10837     * @return {CompositeElement} this
10838     */
10839     replaceElement : function(el, replacement, domReplace){
10840         var index = typeof el == 'number' ? el : this.indexOf(el);
10841         if(index !== -1){
10842             if(domReplace){
10843                 this.elements[index].replaceWith(replacement);
10844             }else{
10845                 this.elements.splice(index, 1, Roo.get(replacement))
10846             }
10847         }
10848         return this;
10849     },
10850
10851     /**
10852      * Removes all elements.
10853      */
10854     clear : function(){
10855         this.elements = [];
10856     }
10857 };
10858 (function(){
10859     Roo.CompositeElement.createCall = function(proto, fnName){
10860         if(!proto[fnName]){
10861             proto[fnName] = function(){
10862                 return this.invoke(fnName, arguments);
10863             };
10864         }
10865     };
10866     for(var fnName in Roo.Element.prototype){
10867         if(typeof Roo.Element.prototype[fnName] == "function"){
10868             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10869         }
10870     };
10871 })();
10872 /*
10873  * Based on:
10874  * Ext JS Library 1.1.1
10875  * Copyright(c) 2006-2007, Ext JS, LLC.
10876  *
10877  * Originally Released Under LGPL - original licence link has changed is not relivant.
10878  *
10879  * Fork - LGPL
10880  * <script type="text/javascript">
10881  */
10882
10883 /**
10884  * @class Roo.CompositeElementLite
10885  * @extends Roo.CompositeElement
10886  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10887  <pre><code>
10888  var els = Roo.select("#some-el div.some-class");
10889  // or select directly from an existing element
10890  var el = Roo.get('some-el');
10891  el.select('div.some-class');
10892
10893  els.setWidth(100); // all elements become 100 width
10894  els.hide(true); // all elements fade out and hide
10895  // or
10896  els.setWidth(100).hide(true);
10897  </code></pre><br><br>
10898  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10899  * actions will be performed on all the elements in this collection.</b>
10900  */
10901 Roo.CompositeElementLite = function(els){
10902     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10903     this.el = new Roo.Element.Flyweight();
10904 };
10905 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10906     addElements : function(els){
10907         if(els){
10908             if(els instanceof Array){
10909                 this.elements = this.elements.concat(els);
10910             }else{
10911                 var yels = this.elements;
10912                 var index = yels.length-1;
10913                 for(var i = 0, len = els.length; i < len; i++) {
10914                     yels[++index] = els[i];
10915                 }
10916             }
10917         }
10918         return this;
10919     },
10920     invoke : function(fn, args){
10921         var els = this.elements;
10922         var el = this.el;
10923         for(var i = 0, len = els.length; i < len; i++) {
10924             el.dom = els[i];
10925                 Roo.Element.prototype[fn].apply(el, args);
10926         }
10927         return this;
10928     },
10929     /**
10930      * Returns a flyweight Element of the dom element object at the specified index
10931      * @param {Number} index
10932      * @return {Roo.Element}
10933      */
10934     item : function(index){
10935         if(!this.elements[index]){
10936             return null;
10937         }
10938         this.el.dom = this.elements[index];
10939         return this.el;
10940     },
10941
10942     // fixes scope with flyweight
10943     addListener : function(eventName, handler, scope, opt){
10944         var els = this.elements;
10945         for(var i = 0, len = els.length; i < len; i++) {
10946             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10947         }
10948         return this;
10949     },
10950
10951     /**
10952     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10953     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10954     * a reference to the dom node, use el.dom.</b>
10955     * @param {Function} fn The function to call
10956     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10957     * @return {CompositeElement} this
10958     */
10959     each : function(fn, scope){
10960         var els = this.elements;
10961         var el = this.el;
10962         for(var i = 0, len = els.length; i < len; i++){
10963             el.dom = els[i];
10964                 if(fn.call(scope || el, el, this, i) === false){
10965                 break;
10966             }
10967         }
10968         return this;
10969     },
10970
10971     indexOf : function(el){
10972         return this.elements.indexOf(Roo.getDom(el));
10973     },
10974
10975     replaceElement : function(el, replacement, domReplace){
10976         var index = typeof el == 'number' ? el : this.indexOf(el);
10977         if(index !== -1){
10978             replacement = Roo.getDom(replacement);
10979             if(domReplace){
10980                 var d = this.elements[index];
10981                 d.parentNode.insertBefore(replacement, d);
10982                 d.parentNode.removeChild(d);
10983             }
10984             this.elements.splice(index, 1, replacement);
10985         }
10986         return this;
10987     }
10988 });
10989 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10990
10991 /*
10992  * Based on:
10993  * Ext JS Library 1.1.1
10994  * Copyright(c) 2006-2007, Ext JS, LLC.
10995  *
10996  * Originally Released Under LGPL - original licence link has changed is not relivant.
10997  *
10998  * Fork - LGPL
10999  * <script type="text/javascript">
11000  */
11001
11002  
11003
11004 /**
11005  * @class Roo.data.Connection
11006  * @extends Roo.util.Observable
11007  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11008  * either to a configured URL, or to a URL specified at request time.<br><br>
11009  * <p>
11010  * Requests made by this class are asynchronous, and will return immediately. No data from
11011  * the server will be available to the statement immediately following the {@link #request} call.
11012  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11013  * <p>
11014  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11015  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11016  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11017  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11018  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11019  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11020  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11021  * standard DOM methods.
11022  * @constructor
11023  * @param {Object} config a configuration object.
11024  */
11025 Roo.data.Connection = function(config){
11026     Roo.apply(this, config);
11027     this.addEvents({
11028         /**
11029          * @event beforerequest
11030          * Fires before a network request is made to retrieve a data object.
11031          * @param {Connection} conn This Connection object.
11032          * @param {Object} options The options config object passed to the {@link #request} method.
11033          */
11034         "beforerequest" : true,
11035         /**
11036          * @event requestcomplete
11037          * Fires if the request was successfully completed.
11038          * @param {Connection} conn This Connection object.
11039          * @param {Object} response The XHR object containing the response data.
11040          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11041          * @param {Object} options The options config object passed to the {@link #request} method.
11042          */
11043         "requestcomplete" : true,
11044         /**
11045          * @event requestexception
11046          * Fires if an error HTTP status was returned from the server.
11047          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11048          * @param {Connection} conn This Connection object.
11049          * @param {Object} response The XHR object containing the response data.
11050          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11051          * @param {Object} options The options config object passed to the {@link #request} method.
11052          */
11053         "requestexception" : true
11054     });
11055     Roo.data.Connection.superclass.constructor.call(this);
11056 };
11057
11058 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11059     /**
11060      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11061      */
11062     /**
11063      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11064      * extra parameters to each request made by this object. (defaults to undefined)
11065      */
11066     /**
11067      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11068      *  to each request made by this object. (defaults to undefined)
11069      */
11070     /**
11071      * @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)
11072      */
11073     /**
11074      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11075      */
11076     timeout : 30000,
11077     /**
11078      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11079      * @type Boolean
11080      */
11081     autoAbort:false,
11082
11083     /**
11084      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11085      * @type Boolean
11086      */
11087     disableCaching: true,
11088
11089     /**
11090      * Sends an HTTP request to a remote server.
11091      * @param {Object} options An object which may contain the following properties:<ul>
11092      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11093      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11094      * request, a url encoded string or a function to call to get either.</li>
11095      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11096      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11097      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11098      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11099      * <li>options {Object} The parameter to the request call.</li>
11100      * <li>success {Boolean} True if the request succeeded.</li>
11101      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11102      * </ul></li>
11103      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11104      * The callback is passed the following parameters:<ul>
11105      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11106      * <li>options {Object} The parameter to the request call.</li>
11107      * </ul></li>
11108      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11109      * The callback is passed the following parameters:<ul>
11110      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11111      * <li>options {Object} The parameter to the request call.</li>
11112      * </ul></li>
11113      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11114      * for the callback function. Defaults to the browser window.</li>
11115      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11116      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11117      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11118      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11119      * params for the post data. Any params will be appended to the URL.</li>
11120      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11121      * </ul>
11122      * @return {Number} transactionId
11123      */
11124     request : function(o){
11125         if(this.fireEvent("beforerequest", this, o) !== false){
11126             var p = o.params;
11127
11128             if(typeof p == "function"){
11129                 p = p.call(o.scope||window, o);
11130             }
11131             if(typeof p == "object"){
11132                 p = Roo.urlEncode(o.params);
11133             }
11134             if(this.extraParams){
11135                 var extras = Roo.urlEncode(this.extraParams);
11136                 p = p ? (p + '&' + extras) : extras;
11137             }
11138
11139             var url = o.url || this.url;
11140             if(typeof url == 'function'){
11141                 url = url.call(o.scope||window, o);
11142             }
11143
11144             if(o.form){
11145                 var form = Roo.getDom(o.form);
11146                 url = url || form.action;
11147
11148                 var enctype = form.getAttribute("enctype");
11149                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11150                     return this.doFormUpload(o, p, url);
11151                 }
11152                 var f = Roo.lib.Ajax.serializeForm(form);
11153                 p = p ? (p + '&' + f) : f;
11154             }
11155
11156             var hs = o.headers;
11157             if(this.defaultHeaders){
11158                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11159                 if(!o.headers){
11160                     o.headers = hs;
11161                 }
11162             }
11163
11164             var cb = {
11165                 success: this.handleResponse,
11166                 failure: this.handleFailure,
11167                 scope: this,
11168                 argument: {options: o},
11169                 timeout : this.timeout
11170             };
11171
11172             var method = o.method||this.method||(p ? "POST" : "GET");
11173
11174             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11175                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11176             }
11177
11178             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11179                 if(o.autoAbort){
11180                     this.abort();
11181                 }
11182             }else if(this.autoAbort !== false){
11183                 this.abort();
11184             }
11185
11186             if((method == 'GET' && p) || o.xmlData){
11187                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11188                 p = '';
11189             }
11190             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11191             return this.transId;
11192         }else{
11193             Roo.callback(o.callback, o.scope, [o, null, null]);
11194             return null;
11195         }
11196     },
11197
11198     /**
11199      * Determine whether this object has a request outstanding.
11200      * @param {Number} transactionId (Optional) defaults to the last transaction
11201      * @return {Boolean} True if there is an outstanding request.
11202      */
11203     isLoading : function(transId){
11204         if(transId){
11205             return Roo.lib.Ajax.isCallInProgress(transId);
11206         }else{
11207             return this.transId ? true : false;
11208         }
11209     },
11210
11211     /**
11212      * Aborts any outstanding request.
11213      * @param {Number} transactionId (Optional) defaults to the last transaction
11214      */
11215     abort : function(transId){
11216         if(transId || this.isLoading()){
11217             Roo.lib.Ajax.abort(transId || this.transId);
11218         }
11219     },
11220
11221     // private
11222     handleResponse : function(response){
11223         this.transId = false;
11224         var options = response.argument.options;
11225         response.argument = options ? options.argument : null;
11226         this.fireEvent("requestcomplete", this, response, options);
11227         Roo.callback(options.success, options.scope, [response, options]);
11228         Roo.callback(options.callback, options.scope, [options, true, response]);
11229     },
11230
11231     // private
11232     handleFailure : function(response, e){
11233         this.transId = false;
11234         var options = response.argument.options;
11235         response.argument = options ? options.argument : null;
11236         this.fireEvent("requestexception", this, response, options, e);
11237         Roo.callback(options.failure, options.scope, [response, options]);
11238         Roo.callback(options.callback, options.scope, [options, false, response]);
11239     },
11240
11241     // private
11242     doFormUpload : function(o, ps, url){
11243         var id = Roo.id();
11244         var frame = document.createElement('iframe');
11245         frame.id = id;
11246         frame.name = id;
11247         frame.className = 'x-hidden';
11248         if(Roo.isIE){
11249             frame.src = Roo.SSL_SECURE_URL;
11250         }
11251         document.body.appendChild(frame);
11252
11253         if(Roo.isIE){
11254            document.frames[id].name = id;
11255         }
11256
11257         var form = Roo.getDom(o.form);
11258         form.target = id;
11259         form.method = 'POST';
11260         form.enctype = form.encoding = 'multipart/form-data';
11261         if(url){
11262             form.action = url;
11263         }
11264
11265         var hiddens, hd;
11266         if(ps){ // add dynamic params
11267             hiddens = [];
11268             ps = Roo.urlDecode(ps, false);
11269             for(var k in ps){
11270                 if(ps.hasOwnProperty(k)){
11271                     hd = document.createElement('input');
11272                     hd.type = 'hidden';
11273                     hd.name = k;
11274                     hd.value = ps[k];
11275                     form.appendChild(hd);
11276                     hiddens.push(hd);
11277                 }
11278             }
11279         }
11280
11281         function cb(){
11282             var r = {  // bogus response object
11283                 responseText : '',
11284                 responseXML : null
11285             };
11286
11287             r.argument = o ? o.argument : null;
11288
11289             try { //
11290                 var doc;
11291                 if(Roo.isIE){
11292                     doc = frame.contentWindow.document;
11293                 }else {
11294                     doc = (frame.contentDocument || window.frames[id].document);
11295                 }
11296                 if(doc && doc.body){
11297                     r.responseText = doc.body.innerHTML;
11298                 }
11299                 if(doc && doc.XMLDocument){
11300                     r.responseXML = doc.XMLDocument;
11301                 }else {
11302                     r.responseXML = doc;
11303                 }
11304             }
11305             catch(e) {
11306                 // ignore
11307             }
11308
11309             Roo.EventManager.removeListener(frame, 'load', cb, this);
11310
11311             this.fireEvent("requestcomplete", this, r, o);
11312             Roo.callback(o.success, o.scope, [r, o]);
11313             Roo.callback(o.callback, o.scope, [o, true, r]);
11314
11315             setTimeout(function(){document.body.removeChild(frame);}, 100);
11316         }
11317
11318         Roo.EventManager.on(frame, 'load', cb, this);
11319         form.submit();
11320
11321         if(hiddens){ // remove dynamic params
11322             for(var i = 0, len = hiddens.length; i < len; i++){
11323                 form.removeChild(hiddens[i]);
11324             }
11325         }
11326     }
11327 });
11328
11329 /**
11330  * @class Roo.Ajax
11331  * @extends Roo.data.Connection
11332  * Global Ajax request class.
11333  *
11334  * @singleton
11335  */
11336 Roo.Ajax = new Roo.data.Connection({
11337     // fix up the docs
11338    /**
11339      * @cfg {String} url @hide
11340      */
11341     /**
11342      * @cfg {Object} extraParams @hide
11343      */
11344     /**
11345      * @cfg {Object} defaultHeaders @hide
11346      */
11347     /**
11348      * @cfg {String} method (Optional) @hide
11349      */
11350     /**
11351      * @cfg {Number} timeout (Optional) @hide
11352      */
11353     /**
11354      * @cfg {Boolean} autoAbort (Optional) @hide
11355      */
11356
11357     /**
11358      * @cfg {Boolean} disableCaching (Optional) @hide
11359      */
11360
11361     /**
11362      * @property  disableCaching
11363      * True to add a unique cache-buster param to GET requests. (defaults to true)
11364      * @type Boolean
11365      */
11366     /**
11367      * @property  url
11368      * The default URL to be used for requests to the server. (defaults to undefined)
11369      * @type String
11370      */
11371     /**
11372      * @property  extraParams
11373      * An object containing properties which are used as
11374      * extra parameters to each request made by this object. (defaults to undefined)
11375      * @type Object
11376      */
11377     /**
11378      * @property  defaultHeaders
11379      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11380      * @type Object
11381      */
11382     /**
11383      * @property  method
11384      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11385      * @type String
11386      */
11387     /**
11388      * @property  timeout
11389      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11390      * @type Number
11391      */
11392
11393     /**
11394      * @property  autoAbort
11395      * Whether a new request should abort any pending requests. (defaults to false)
11396      * @type Boolean
11397      */
11398     autoAbort : false,
11399
11400     /**
11401      * Serialize the passed form into a url encoded string
11402      * @param {String/HTMLElement} form
11403      * @return {String}
11404      */
11405     serializeForm : function(form){
11406         return Roo.lib.Ajax.serializeForm(form);
11407     }
11408 });/*
11409  * Based on:
11410  * Ext JS Library 1.1.1
11411  * Copyright(c) 2006-2007, Ext JS, LLC.
11412  *
11413  * Originally Released Under LGPL - original licence link has changed is not relivant.
11414  *
11415  * Fork - LGPL
11416  * <script type="text/javascript">
11417  */
11418  
11419 /**
11420  * @class Roo.Ajax
11421  * @extends Roo.data.Connection
11422  * Global Ajax request class.
11423  *
11424  * @instanceOf  Roo.data.Connection
11425  */
11426 Roo.Ajax = new Roo.data.Connection({
11427     // fix up the docs
11428     
11429     /**
11430      * fix up scoping
11431      * @scope Roo.Ajax
11432      */
11433     
11434    /**
11435      * @cfg {String} url @hide
11436      */
11437     /**
11438      * @cfg {Object} extraParams @hide
11439      */
11440     /**
11441      * @cfg {Object} defaultHeaders @hide
11442      */
11443     /**
11444      * @cfg {String} method (Optional) @hide
11445      */
11446     /**
11447      * @cfg {Number} timeout (Optional) @hide
11448      */
11449     /**
11450      * @cfg {Boolean} autoAbort (Optional) @hide
11451      */
11452
11453     /**
11454      * @cfg {Boolean} disableCaching (Optional) @hide
11455      */
11456
11457     /**
11458      * @property  disableCaching
11459      * True to add a unique cache-buster param to GET requests. (defaults to true)
11460      * @type Boolean
11461      */
11462     /**
11463      * @property  url
11464      * The default URL to be used for requests to the server. (defaults to undefined)
11465      * @type String
11466      */
11467     /**
11468      * @property  extraParams
11469      * An object containing properties which are used as
11470      * extra parameters to each request made by this object. (defaults to undefined)
11471      * @type Object
11472      */
11473     /**
11474      * @property  defaultHeaders
11475      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11476      * @type Object
11477      */
11478     /**
11479      * @property  method
11480      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11481      * @type String
11482      */
11483     /**
11484      * @property  timeout
11485      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11486      * @type Number
11487      */
11488
11489     /**
11490      * @property  autoAbort
11491      * Whether a new request should abort any pending requests. (defaults to false)
11492      * @type Boolean
11493      */
11494     autoAbort : false,
11495
11496     /**
11497      * Serialize the passed form into a url encoded string
11498      * @param {String/HTMLElement} form
11499      * @return {String}
11500      */
11501     serializeForm : function(form){
11502         return Roo.lib.Ajax.serializeForm(form);
11503     }
11504 });/*
11505  * Based on:
11506  * Ext JS Library 1.1.1
11507  * Copyright(c) 2006-2007, Ext JS, LLC.
11508  *
11509  * Originally Released Under LGPL - original licence link has changed is not relivant.
11510  *
11511  * Fork - LGPL
11512  * <script type="text/javascript">
11513  */
11514
11515  
11516 /**
11517  * @class Roo.UpdateManager
11518  * @extends Roo.util.Observable
11519  * Provides AJAX-style update for Element object.<br><br>
11520  * Usage:<br>
11521  * <pre><code>
11522  * // Get it from a Roo.Element object
11523  * var el = Roo.get("foo");
11524  * var mgr = el.getUpdateManager();
11525  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11526  * ...
11527  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11528  * <br>
11529  * // or directly (returns the same UpdateManager instance)
11530  * var mgr = new Roo.UpdateManager("myElementId");
11531  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11532  * mgr.on("update", myFcnNeedsToKnow);
11533  * <br>
11534    // short handed call directly from the element object
11535    Roo.get("foo").load({
11536         url: "bar.php",
11537         scripts:true,
11538         params: "for=bar",
11539         text: "Loading Foo..."
11540    });
11541  * </code></pre>
11542  * @constructor
11543  * Create new UpdateManager directly.
11544  * @param {String/HTMLElement/Roo.Element} el The element to update
11545  * @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).
11546  */
11547 Roo.UpdateManager = function(el, forceNew){
11548     el = Roo.get(el);
11549     if(!forceNew && el.updateManager){
11550         return el.updateManager;
11551     }
11552     /**
11553      * The Element object
11554      * @type Roo.Element
11555      */
11556     this.el = el;
11557     /**
11558      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11559      * @type String
11560      */
11561     this.defaultUrl = null;
11562
11563     this.addEvents({
11564         /**
11565          * @event beforeupdate
11566          * Fired before an update is made, return false from your handler and the update is cancelled.
11567          * @param {Roo.Element} el
11568          * @param {String/Object/Function} url
11569          * @param {String/Object} params
11570          */
11571         "beforeupdate": true,
11572         /**
11573          * @event update
11574          * Fired after successful update is made.
11575          * @param {Roo.Element} el
11576          * @param {Object} oResponseObject The response Object
11577          */
11578         "update": true,
11579         /**
11580          * @event failure
11581          * Fired on update failure.
11582          * @param {Roo.Element} el
11583          * @param {Object} oResponseObject The response Object
11584          */
11585         "failure": true
11586     });
11587     var d = Roo.UpdateManager.defaults;
11588     /**
11589      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11590      * @type String
11591      */
11592     this.sslBlankUrl = d.sslBlankUrl;
11593     /**
11594      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11595      * @type Boolean
11596      */
11597     this.disableCaching = d.disableCaching;
11598     /**
11599      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11600      * @type String
11601      */
11602     this.indicatorText = d.indicatorText;
11603     /**
11604      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11605      * @type String
11606      */
11607     this.showLoadIndicator = d.showLoadIndicator;
11608     /**
11609      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11610      * @type Number
11611      */
11612     this.timeout = d.timeout;
11613
11614     /**
11615      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11616      * @type Boolean
11617      */
11618     this.loadScripts = d.loadScripts;
11619
11620     /**
11621      * Transaction object of current executing transaction
11622      */
11623     this.transaction = null;
11624
11625     /**
11626      * @private
11627      */
11628     this.autoRefreshProcId = null;
11629     /**
11630      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11631      * @type Function
11632      */
11633     this.refreshDelegate = this.refresh.createDelegate(this);
11634     /**
11635      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11636      * @type Function
11637      */
11638     this.updateDelegate = this.update.createDelegate(this);
11639     /**
11640      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11641      * @type Function
11642      */
11643     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11644     /**
11645      * @private
11646      */
11647     this.successDelegate = this.processSuccess.createDelegate(this);
11648     /**
11649      * @private
11650      */
11651     this.failureDelegate = this.processFailure.createDelegate(this);
11652
11653     if(!this.renderer){
11654      /**
11655       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11656       */
11657     this.renderer = new Roo.UpdateManager.BasicRenderer();
11658     }
11659     
11660     Roo.UpdateManager.superclass.constructor.call(this);
11661 };
11662
11663 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11664     /**
11665      * Get the Element this UpdateManager is bound to
11666      * @return {Roo.Element} The element
11667      */
11668     getEl : function(){
11669         return this.el;
11670     },
11671     /**
11672      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11673      * @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:
11674 <pre><code>
11675 um.update({<br/>
11676     url: "your-url.php",<br/>
11677     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11678     callback: yourFunction,<br/>
11679     scope: yourObject, //(optional scope)  <br/>
11680     discardUrl: false, <br/>
11681     nocache: false,<br/>
11682     text: "Loading...",<br/>
11683     timeout: 30,<br/>
11684     scripts: false<br/>
11685 });
11686 </code></pre>
11687      * The only required property is url. The optional properties nocache, text and scripts
11688      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11689      * @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}
11690      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11691      * @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.
11692      */
11693     update : function(url, params, callback, discardUrl){
11694         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11695             var method = this.method, cfg;
11696             if(typeof url == "object"){ // must be config object
11697                 cfg = url;
11698                 url = cfg.url;
11699                 params = params || cfg.params;
11700                 callback = callback || cfg.callback;
11701                 discardUrl = discardUrl || cfg.discardUrl;
11702                 if(callback && cfg.scope){
11703                     callback = callback.createDelegate(cfg.scope);
11704                 }
11705                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11706                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11707                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11708                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11709                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11710             }
11711             this.showLoading();
11712             if(!discardUrl){
11713                 this.defaultUrl = url;
11714             }
11715             if(typeof url == "function"){
11716                 url = url.call(this);
11717             }
11718
11719             method = method || (params ? "POST" : "GET");
11720             if(method == "GET"){
11721                 url = this.prepareUrl(url);
11722             }
11723
11724             var o = Roo.apply(cfg ||{}, {
11725                 url : url,
11726                 params: params,
11727                 success: this.successDelegate,
11728                 failure: this.failureDelegate,
11729                 callback: undefined,
11730                 timeout: (this.timeout*1000),
11731                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11732             });
11733
11734             this.transaction = Roo.Ajax.request(o);
11735         }
11736     },
11737
11738     /**
11739      * 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.
11740      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11741      * @param {String/HTMLElement} form The form Id or form element
11742      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11743      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11744      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11745      */
11746     formUpdate : function(form, url, reset, callback){
11747         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11748             if(typeof url == "function"){
11749                 url = url.call(this);
11750             }
11751             form = Roo.getDom(form);
11752             this.transaction = Roo.Ajax.request({
11753                 form: form,
11754                 url:url,
11755                 success: this.successDelegate,
11756                 failure: this.failureDelegate,
11757                 timeout: (this.timeout*1000),
11758                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11759             });
11760             this.showLoading.defer(1, this);
11761         }
11762     },
11763
11764     /**
11765      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11766      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11767      */
11768     refresh : function(callback){
11769         if(this.defaultUrl == null){
11770             return;
11771         }
11772         this.update(this.defaultUrl, null, callback, true);
11773     },
11774
11775     /**
11776      * Set this element to auto refresh.
11777      * @param {Number} interval How often to update (in seconds).
11778      * @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)
11779      * @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}
11780      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11781      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11782      */
11783     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11784         if(refreshNow){
11785             this.update(url || this.defaultUrl, params, callback, true);
11786         }
11787         if(this.autoRefreshProcId){
11788             clearInterval(this.autoRefreshProcId);
11789         }
11790         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11791     },
11792
11793     /**
11794      * Stop auto refresh on this element.
11795      */
11796      stopAutoRefresh : function(){
11797         if(this.autoRefreshProcId){
11798             clearInterval(this.autoRefreshProcId);
11799             delete this.autoRefreshProcId;
11800         }
11801     },
11802
11803     isAutoRefreshing : function(){
11804        return this.autoRefreshProcId ? true : false;
11805     },
11806     /**
11807      * Called to update the element to "Loading" state. Override to perform custom action.
11808      */
11809     showLoading : function(){
11810         if(this.showLoadIndicator){
11811             this.el.update(this.indicatorText);
11812         }
11813     },
11814
11815     /**
11816      * Adds unique parameter to query string if disableCaching = true
11817      * @private
11818      */
11819     prepareUrl : function(url){
11820         if(this.disableCaching){
11821             var append = "_dc=" + (new Date().getTime());
11822             if(url.indexOf("?") !== -1){
11823                 url += "&" + append;
11824             }else{
11825                 url += "?" + append;
11826             }
11827         }
11828         return url;
11829     },
11830
11831     /**
11832      * @private
11833      */
11834     processSuccess : function(response){
11835         this.transaction = null;
11836         if(response.argument.form && response.argument.reset){
11837             try{ // put in try/catch since some older FF releases had problems with this
11838                 response.argument.form.reset();
11839             }catch(e){}
11840         }
11841         if(this.loadScripts){
11842             this.renderer.render(this.el, response, this,
11843                 this.updateComplete.createDelegate(this, [response]));
11844         }else{
11845             this.renderer.render(this.el, response, this);
11846             this.updateComplete(response);
11847         }
11848     },
11849
11850     updateComplete : function(response){
11851         this.fireEvent("update", this.el, response);
11852         if(typeof response.argument.callback == "function"){
11853             response.argument.callback(this.el, true, response);
11854         }
11855     },
11856
11857     /**
11858      * @private
11859      */
11860     processFailure : function(response){
11861         this.transaction = null;
11862         this.fireEvent("failure", this.el, response);
11863         if(typeof response.argument.callback == "function"){
11864             response.argument.callback(this.el, false, response);
11865         }
11866     },
11867
11868     /**
11869      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11870      * @param {Object} renderer The object implementing the render() method
11871      */
11872     setRenderer : function(renderer){
11873         this.renderer = renderer;
11874     },
11875
11876     getRenderer : function(){
11877        return this.renderer;
11878     },
11879
11880     /**
11881      * Set the defaultUrl used for updates
11882      * @param {String/Function} defaultUrl The url or a function to call to get the url
11883      */
11884     setDefaultUrl : function(defaultUrl){
11885         this.defaultUrl = defaultUrl;
11886     },
11887
11888     /**
11889      * Aborts the executing transaction
11890      */
11891     abort : function(){
11892         if(this.transaction){
11893             Roo.Ajax.abort(this.transaction);
11894         }
11895     },
11896
11897     /**
11898      * Returns true if an update is in progress
11899      * @return {Boolean}
11900      */
11901     isUpdating : function(){
11902         if(this.transaction){
11903             return Roo.Ajax.isLoading(this.transaction);
11904         }
11905         return false;
11906     }
11907 });
11908
11909 /**
11910  * @class Roo.UpdateManager.defaults
11911  * @static (not really - but it helps the doc tool)
11912  * The defaults collection enables customizing the default properties of UpdateManager
11913  */
11914    Roo.UpdateManager.defaults = {
11915        /**
11916          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11917          * @type Number
11918          */
11919          timeout : 30,
11920
11921          /**
11922          * True to process scripts by default (Defaults to false).
11923          * @type Boolean
11924          */
11925         loadScripts : false,
11926
11927         /**
11928         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11929         * @type String
11930         */
11931         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11932         /**
11933          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11934          * @type Boolean
11935          */
11936         disableCaching : false,
11937         /**
11938          * Whether to show indicatorText when loading (Defaults to true).
11939          * @type Boolean
11940          */
11941         showLoadIndicator : true,
11942         /**
11943          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11944          * @type String
11945          */
11946         indicatorText : '<div class="loading-indicator">Loading...</div>'
11947    };
11948
11949 /**
11950  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11951  *Usage:
11952  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11953  * @param {String/HTMLElement/Roo.Element} el The element to update
11954  * @param {String} url The url
11955  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11956  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11957  * @static
11958  * @deprecated
11959  * @member Roo.UpdateManager
11960  */
11961 Roo.UpdateManager.updateElement = function(el, url, params, options){
11962     var um = Roo.get(el, true).getUpdateManager();
11963     Roo.apply(um, options);
11964     um.update(url, params, options ? options.callback : null);
11965 };
11966 // alias for backwards compat
11967 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11968 /**
11969  * @class Roo.UpdateManager.BasicRenderer
11970  * Default Content renderer. Updates the elements innerHTML with the responseText.
11971  */
11972 Roo.UpdateManager.BasicRenderer = function(){};
11973
11974 Roo.UpdateManager.BasicRenderer.prototype = {
11975     /**
11976      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11977      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11978      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11979      * @param {Roo.Element} el The element being rendered
11980      * @param {Object} response The YUI Connect response object
11981      * @param {UpdateManager} updateManager The calling update manager
11982      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11983      */
11984      render : function(el, response, updateManager, callback){
11985         el.update(response.responseText, updateManager.loadScripts, callback);
11986     }
11987 };
11988 /*
11989  * Based on:
11990  * Ext JS Library 1.1.1
11991  * Copyright(c) 2006-2007, Ext JS, LLC.
11992  *
11993  * Originally Released Under LGPL - original licence link has changed is not relivant.
11994  *
11995  * Fork - LGPL
11996  * <script type="text/javascript">
11997  */
11998
11999 /**
12000  * @class Roo.util.DelayedTask
12001  * Provides a convenient method of performing setTimeout where a new
12002  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12003  * You can use this class to buffer
12004  * the keypress events for a certain number of milliseconds, and perform only if they stop
12005  * for that amount of time.
12006  * @constructor The parameters to this constructor serve as defaults and are not required.
12007  * @param {Function} fn (optional) The default function to timeout
12008  * @param {Object} scope (optional) The default scope of that timeout
12009  * @param {Array} args (optional) The default Array of arguments
12010  */
12011 Roo.util.DelayedTask = function(fn, scope, args){
12012     var id = null, d, t;
12013
12014     var call = function(){
12015         var now = new Date().getTime();
12016         if(now - t >= d){
12017             clearInterval(id);
12018             id = null;
12019             fn.apply(scope, args || []);
12020         }
12021     };
12022     /**
12023      * Cancels any pending timeout and queues a new one
12024      * @param {Number} delay The milliseconds to delay
12025      * @param {Function} newFn (optional) Overrides function passed to constructor
12026      * @param {Object} newScope (optional) Overrides scope passed to constructor
12027      * @param {Array} newArgs (optional) Overrides args passed to constructor
12028      */
12029     this.delay = function(delay, newFn, newScope, newArgs){
12030         if(id && delay != d){
12031             this.cancel();
12032         }
12033         d = delay;
12034         t = new Date().getTime();
12035         fn = newFn || fn;
12036         scope = newScope || scope;
12037         args = newArgs || args;
12038         if(!id){
12039             id = setInterval(call, d);
12040         }
12041     };
12042
12043     /**
12044      * Cancel the last queued timeout
12045      */
12046     this.cancel = function(){
12047         if(id){
12048             clearInterval(id);
12049             id = null;
12050         }
12051     };
12052 };/*
12053  * Based on:
12054  * Ext JS Library 1.1.1
12055  * Copyright(c) 2006-2007, Ext JS, LLC.
12056  *
12057  * Originally Released Under LGPL - original licence link has changed is not relivant.
12058  *
12059  * Fork - LGPL
12060  * <script type="text/javascript">
12061  */
12062  
12063  
12064 Roo.util.TaskRunner = function(interval){
12065     interval = interval || 10;
12066     var tasks = [], removeQueue = [];
12067     var id = 0;
12068     var running = false;
12069
12070     var stopThread = function(){
12071         running = false;
12072         clearInterval(id);
12073         id = 0;
12074     };
12075
12076     var startThread = function(){
12077         if(!running){
12078             running = true;
12079             id = setInterval(runTasks, interval);
12080         }
12081     };
12082
12083     var removeTask = function(task){
12084         removeQueue.push(task);
12085         if(task.onStop){
12086             task.onStop();
12087         }
12088     };
12089
12090     var runTasks = function(){
12091         if(removeQueue.length > 0){
12092             for(var i = 0, len = removeQueue.length; i < len; i++){
12093                 tasks.remove(removeQueue[i]);
12094             }
12095             removeQueue = [];
12096             if(tasks.length < 1){
12097                 stopThread();
12098                 return;
12099             }
12100         }
12101         var now = new Date().getTime();
12102         for(var i = 0, len = tasks.length; i < len; ++i){
12103             var t = tasks[i];
12104             var itime = now - t.taskRunTime;
12105             if(t.interval <= itime){
12106                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12107                 t.taskRunTime = now;
12108                 if(rt === false || t.taskRunCount === t.repeat){
12109                     removeTask(t);
12110                     return;
12111                 }
12112             }
12113             if(t.duration && t.duration <= (now - t.taskStartTime)){
12114                 removeTask(t);
12115             }
12116         }
12117     };
12118
12119     /**
12120      * Queues a new task.
12121      * @param {Object} task
12122      */
12123     this.start = function(task){
12124         tasks.push(task);
12125         task.taskStartTime = new Date().getTime();
12126         task.taskRunTime = 0;
12127         task.taskRunCount = 0;
12128         startThread();
12129         return task;
12130     };
12131
12132     this.stop = function(task){
12133         removeTask(task);
12134         return task;
12135     };
12136
12137     this.stopAll = function(){
12138         stopThread();
12139         for(var i = 0, len = tasks.length; i < len; i++){
12140             if(tasks[i].onStop){
12141                 tasks[i].onStop();
12142             }
12143         }
12144         tasks = [];
12145         removeQueue = [];
12146     };
12147 };
12148
12149 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12150  * Based on:
12151  * Ext JS Library 1.1.1
12152  * Copyright(c) 2006-2007, Ext JS, LLC.
12153  *
12154  * Originally Released Under LGPL - original licence link has changed is not relivant.
12155  *
12156  * Fork - LGPL
12157  * <script type="text/javascript">
12158  */
12159
12160  
12161 /**
12162  * @class Roo.util.MixedCollection
12163  * @extends Roo.util.Observable
12164  * A Collection class that maintains both numeric indexes and keys and exposes events.
12165  * @constructor
12166  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12167  * collection (defaults to false)
12168  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12169  * and return the key value for that item.  This is used when available to look up the key on items that
12170  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12171  * equivalent to providing an implementation for the {@link #getKey} method.
12172  */
12173 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12174     this.items = [];
12175     this.map = {};
12176     this.keys = [];
12177     this.length = 0;
12178     this.addEvents({
12179         /**
12180          * @event clear
12181          * Fires when the collection is cleared.
12182          */
12183         "clear" : true,
12184         /**
12185          * @event add
12186          * Fires when an item is added to the collection.
12187          * @param {Number} index The index at which the item was added.
12188          * @param {Object} o The item added.
12189          * @param {String} key The key associated with the added item.
12190          */
12191         "add" : true,
12192         /**
12193          * @event replace
12194          * Fires when an item is replaced in the collection.
12195          * @param {String} key he key associated with the new added.
12196          * @param {Object} old The item being replaced.
12197          * @param {Object} new The new item.
12198          */
12199         "replace" : true,
12200         /**
12201          * @event remove
12202          * Fires when an item is removed from the collection.
12203          * @param {Object} o The item being removed.
12204          * @param {String} key (optional) The key associated with the removed item.
12205          */
12206         "remove" : true,
12207         "sort" : true
12208     });
12209     this.allowFunctions = allowFunctions === true;
12210     if(keyFn){
12211         this.getKey = keyFn;
12212     }
12213     Roo.util.MixedCollection.superclass.constructor.call(this);
12214 };
12215
12216 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12217     allowFunctions : false,
12218     
12219 /**
12220  * Adds an item to the collection.
12221  * @param {String} key The key to associate with the item
12222  * @param {Object} o The item to add.
12223  * @return {Object} The item added.
12224  */
12225     add : function(key, o){
12226         if(arguments.length == 1){
12227             o = arguments[0];
12228             key = this.getKey(o);
12229         }
12230         if(typeof key == "undefined" || key === null){
12231             this.length++;
12232             this.items.push(o);
12233             this.keys.push(null);
12234         }else{
12235             var old = this.map[key];
12236             if(old){
12237                 return this.replace(key, o);
12238             }
12239             this.length++;
12240             this.items.push(o);
12241             this.map[key] = o;
12242             this.keys.push(key);
12243         }
12244         this.fireEvent("add", this.length-1, o, key);
12245         return o;
12246     },
12247    
12248 /**
12249   * MixedCollection has a generic way to fetch keys if you implement getKey.
12250 <pre><code>
12251 // normal way
12252 var mc = new Roo.util.MixedCollection();
12253 mc.add(someEl.dom.id, someEl);
12254 mc.add(otherEl.dom.id, otherEl);
12255 //and so on
12256
12257 // using getKey
12258 var mc = new Roo.util.MixedCollection();
12259 mc.getKey = function(el){
12260    return el.dom.id;
12261 };
12262 mc.add(someEl);
12263 mc.add(otherEl);
12264
12265 // or via the constructor
12266 var mc = new Roo.util.MixedCollection(false, function(el){
12267    return el.dom.id;
12268 });
12269 mc.add(someEl);
12270 mc.add(otherEl);
12271 </code></pre>
12272  * @param o {Object} The item for which to find the key.
12273  * @return {Object} The key for the passed item.
12274  */
12275     getKey : function(o){
12276          return o.id; 
12277     },
12278    
12279 /**
12280  * Replaces an item in the collection.
12281  * @param {String} key The key associated with the item to replace, or the item to replace.
12282  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12283  * @return {Object}  The new item.
12284  */
12285     replace : function(key, o){
12286         if(arguments.length == 1){
12287             o = arguments[0];
12288             key = this.getKey(o);
12289         }
12290         var old = this.item(key);
12291         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12292              return this.add(key, o);
12293         }
12294         var index = this.indexOfKey(key);
12295         this.items[index] = o;
12296         this.map[key] = o;
12297         this.fireEvent("replace", key, old, o);
12298         return o;
12299     },
12300    
12301 /**
12302  * Adds all elements of an Array or an Object to the collection.
12303  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12304  * an Array of values, each of which are added to the collection.
12305  */
12306     addAll : function(objs){
12307         if(arguments.length > 1 || objs instanceof Array){
12308             var args = arguments.length > 1 ? arguments : objs;
12309             for(var i = 0, len = args.length; i < len; i++){
12310                 this.add(args[i]);
12311             }
12312         }else{
12313             for(var key in objs){
12314                 if(this.allowFunctions || typeof objs[key] != "function"){
12315                     this.add(key, objs[key]);
12316                 }
12317             }
12318         }
12319     },
12320    
12321 /**
12322  * Executes the specified function once for every item in the collection, passing each
12323  * item as the first and only parameter. returning false from the function will stop the iteration.
12324  * @param {Function} fn The function to execute for each item.
12325  * @param {Object} scope (optional) The scope in which to execute the function.
12326  */
12327     each : function(fn, scope){
12328         var items = [].concat(this.items); // each safe for removal
12329         for(var i = 0, len = items.length; i < len; i++){
12330             if(fn.call(scope || items[i], items[i], i, len) === false){
12331                 break;
12332             }
12333         }
12334     },
12335    
12336 /**
12337  * Executes the specified function once for every key in the collection, passing each
12338  * key, and its associated item as the first two parameters.
12339  * @param {Function} fn The function to execute for each item.
12340  * @param {Object} scope (optional) The scope in which to execute the function.
12341  */
12342     eachKey : function(fn, scope){
12343         for(var i = 0, len = this.keys.length; i < len; i++){
12344             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12345         }
12346     },
12347    
12348 /**
12349  * Returns the first item in the collection which elicits a true return value from the
12350  * passed selection function.
12351  * @param {Function} fn The selection function to execute for each item.
12352  * @param {Object} scope (optional) The scope in which to execute the function.
12353  * @return {Object} The first item in the collection which returned true from the selection function.
12354  */
12355     find : function(fn, scope){
12356         for(var i = 0, len = this.items.length; i < len; i++){
12357             if(fn.call(scope || window, this.items[i], this.keys[i])){
12358                 return this.items[i];
12359             }
12360         }
12361         return null;
12362     },
12363    
12364 /**
12365  * Inserts an item at the specified index in the collection.
12366  * @param {Number} index The index to insert the item at.
12367  * @param {String} key The key to associate with the new item, or the item itself.
12368  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12369  * @return {Object} The item inserted.
12370  */
12371     insert : function(index, key, o){
12372         if(arguments.length == 2){
12373             o = arguments[1];
12374             key = this.getKey(o);
12375         }
12376         if(index >= this.length){
12377             return this.add(key, o);
12378         }
12379         this.length++;
12380         this.items.splice(index, 0, o);
12381         if(typeof key != "undefined" && key != null){
12382             this.map[key] = o;
12383         }
12384         this.keys.splice(index, 0, key);
12385         this.fireEvent("add", index, o, key);
12386         return o;
12387     },
12388    
12389 /**
12390  * Removed an item from the collection.
12391  * @param {Object} o The item to remove.
12392  * @return {Object} The item removed.
12393  */
12394     remove : function(o){
12395         return this.removeAt(this.indexOf(o));
12396     },
12397    
12398 /**
12399  * Remove an item from a specified index in the collection.
12400  * @param {Number} index The index within the collection of the item to remove.
12401  */
12402     removeAt : function(index){
12403         if(index < this.length && index >= 0){
12404             this.length--;
12405             var o = this.items[index];
12406             this.items.splice(index, 1);
12407             var key = this.keys[index];
12408             if(typeof key != "undefined"){
12409                 delete this.map[key];
12410             }
12411             this.keys.splice(index, 1);
12412             this.fireEvent("remove", o, key);
12413         }
12414     },
12415    
12416 /**
12417  * Removed an item associated with the passed key fom the collection.
12418  * @param {String} key The key of the item to remove.
12419  */
12420     removeKey : function(key){
12421         return this.removeAt(this.indexOfKey(key));
12422     },
12423    
12424 /**
12425  * Returns the number of items in the collection.
12426  * @return {Number} the number of items in the collection.
12427  */
12428     getCount : function(){
12429         return this.length; 
12430     },
12431    
12432 /**
12433  * Returns index within the collection of the passed Object.
12434  * @param {Object} o The item to find the index of.
12435  * @return {Number} index of the item.
12436  */
12437     indexOf : function(o){
12438         if(!this.items.indexOf){
12439             for(var i = 0, len = this.items.length; i < len; i++){
12440                 if(this.items[i] == o) return i;
12441             }
12442             return -1;
12443         }else{
12444             return this.items.indexOf(o);
12445         }
12446     },
12447    
12448 /**
12449  * Returns index within the collection of the passed key.
12450  * @param {String} key The key to find the index of.
12451  * @return {Number} index of the key.
12452  */
12453     indexOfKey : function(key){
12454         if(!this.keys.indexOf){
12455             for(var i = 0, len = this.keys.length; i < len; i++){
12456                 if(this.keys[i] == key) return i;
12457             }
12458             return -1;
12459         }else{
12460             return this.keys.indexOf(key);
12461         }
12462     },
12463    
12464 /**
12465  * Returns the item associated with the passed key OR index. Key has priority over index.
12466  * @param {String/Number} key The key or index of the item.
12467  * @return {Object} The item associated with the passed key.
12468  */
12469     item : function(key){
12470         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12471         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12472     },
12473     
12474 /**
12475  * Returns the item at the specified index.
12476  * @param {Number} index The index of the item.
12477  * @return {Object}
12478  */
12479     itemAt : function(index){
12480         return this.items[index];
12481     },
12482     
12483 /**
12484  * Returns the item associated with the passed key.
12485  * @param {String/Number} key The key of the item.
12486  * @return {Object} The item associated with the passed key.
12487  */
12488     key : function(key){
12489         return this.map[key];
12490     },
12491    
12492 /**
12493  * Returns true if the collection contains the passed Object as an item.
12494  * @param {Object} o  The Object to look for in the collection.
12495  * @return {Boolean} True if the collection contains the Object as an item.
12496  */
12497     contains : function(o){
12498         return this.indexOf(o) != -1;
12499     },
12500    
12501 /**
12502  * Returns true if the collection contains the passed Object as a key.
12503  * @param {String} key The key to look for in the collection.
12504  * @return {Boolean} True if the collection contains the Object as a key.
12505  */
12506     containsKey : function(key){
12507         return typeof this.map[key] != "undefined";
12508     },
12509    
12510 /**
12511  * Removes all items from the collection.
12512  */
12513     clear : function(){
12514         this.length = 0;
12515         this.items = [];
12516         this.keys = [];
12517         this.map = {};
12518         this.fireEvent("clear");
12519     },
12520    
12521 /**
12522  * Returns the first item in the collection.
12523  * @return {Object} the first item in the collection..
12524  */
12525     first : function(){
12526         return this.items[0]; 
12527     },
12528    
12529 /**
12530  * Returns the last item in the collection.
12531  * @return {Object} the last item in the collection..
12532  */
12533     last : function(){
12534         return this.items[this.length-1];   
12535     },
12536     
12537     _sort : function(property, dir, fn){
12538         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12539         fn = fn || function(a, b){
12540             return a-b;
12541         };
12542         var c = [], k = this.keys, items = this.items;
12543         for(var i = 0, len = items.length; i < len; i++){
12544             c[c.length] = {key: k[i], value: items[i], index: i};
12545         }
12546         c.sort(function(a, b){
12547             var v = fn(a[property], b[property]) * dsc;
12548             if(v == 0){
12549                 v = (a.index < b.index ? -1 : 1);
12550             }
12551             return v;
12552         });
12553         for(var i = 0, len = c.length; i < len; i++){
12554             items[i] = c[i].value;
12555             k[i] = c[i].key;
12556         }
12557         this.fireEvent("sort", this);
12558     },
12559     
12560     /**
12561      * Sorts this collection with the passed comparison function
12562      * @param {String} direction (optional) "ASC" or "DESC"
12563      * @param {Function} fn (optional) comparison function
12564      */
12565     sort : function(dir, fn){
12566         this._sort("value", dir, fn);
12567     },
12568     
12569     /**
12570      * Sorts this collection by keys
12571      * @param {String} direction (optional) "ASC" or "DESC"
12572      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12573      */
12574     keySort : function(dir, fn){
12575         this._sort("key", dir, fn || function(a, b){
12576             return String(a).toUpperCase()-String(b).toUpperCase();
12577         });
12578     },
12579     
12580     /**
12581      * Returns a range of items in this collection
12582      * @param {Number} startIndex (optional) defaults to 0
12583      * @param {Number} endIndex (optional) default to the last item
12584      * @return {Array} An array of items
12585      */
12586     getRange : function(start, end){
12587         var items = this.items;
12588         if(items.length < 1){
12589             return [];
12590         }
12591         start = start || 0;
12592         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12593         var r = [];
12594         if(start <= end){
12595             for(var i = start; i <= end; i++) {
12596                     r[r.length] = items[i];
12597             }
12598         }else{
12599             for(var i = start; i >= end; i--) {
12600                     r[r.length] = items[i];
12601             }
12602         }
12603         return r;
12604     },
12605         
12606     /**
12607      * Filter the <i>objects</i> in this collection by a specific property. 
12608      * Returns a new collection that has been filtered.
12609      * @param {String} property A property on your objects
12610      * @param {String/RegExp} value Either string that the property values 
12611      * should start with or a RegExp to test against the property
12612      * @return {MixedCollection} The new filtered collection
12613      */
12614     filter : function(property, value){
12615         if(!value.exec){ // not a regex
12616             value = String(value);
12617             if(value.length == 0){
12618                 return this.clone();
12619             }
12620             value = new RegExp("^" + Roo.escapeRe(value), "i");
12621         }
12622         return this.filterBy(function(o){
12623             return o && value.test(o[property]);
12624         });
12625         },
12626     
12627     /**
12628      * Filter by a function. * Returns a new collection that has been filtered.
12629      * The passed function will be called with each 
12630      * object in the collection. If the function returns true, the value is included 
12631      * otherwise it is filtered.
12632      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12633      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12634      * @return {MixedCollection} The new filtered collection
12635      */
12636     filterBy : function(fn, scope){
12637         var r = new Roo.util.MixedCollection();
12638         r.getKey = this.getKey;
12639         var k = this.keys, it = this.items;
12640         for(var i = 0, len = it.length; i < len; i++){
12641             if(fn.call(scope||this, it[i], k[i])){
12642                                 r.add(k[i], it[i]);
12643                         }
12644         }
12645         return r;
12646     },
12647     
12648     /**
12649      * Creates a duplicate of this collection
12650      * @return {MixedCollection}
12651      */
12652     clone : function(){
12653         var r = new Roo.util.MixedCollection();
12654         var k = this.keys, it = this.items;
12655         for(var i = 0, len = it.length; i < len; i++){
12656             r.add(k[i], it[i]);
12657         }
12658         r.getKey = this.getKey;
12659         return r;
12660     }
12661 });
12662 /**
12663  * Returns the item associated with the passed key or index.
12664  * @method
12665  * @param {String/Number} key The key or index of the item.
12666  * @return {Object} The item associated with the passed key.
12667  */
12668 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12669  * Based on:
12670  * Ext JS Library 1.1.1
12671  * Copyright(c) 2006-2007, Ext JS, LLC.
12672  *
12673  * Originally Released Under LGPL - original licence link has changed is not relivant.
12674  *
12675  * Fork - LGPL
12676  * <script type="text/javascript">
12677  */
12678 /**
12679  * @class Roo.util.JSON
12680  * Modified version of Douglas Crockford"s json.js that doesn"t
12681  * mess with the Object prototype 
12682  * http://www.json.org/js.html
12683  * @singleton
12684  */
12685 Roo.util.JSON = new (function(){
12686     var useHasOwn = {}.hasOwnProperty ? true : false;
12687     
12688     // crashes Safari in some instances
12689     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12690     
12691     var pad = function(n) {
12692         return n < 10 ? "0" + n : n;
12693     };
12694     
12695     var m = {
12696         "\b": '\\b',
12697         "\t": '\\t',
12698         "\n": '\\n',
12699         "\f": '\\f',
12700         "\r": '\\r',
12701         '"' : '\\"',
12702         "\\": '\\\\'
12703     };
12704
12705     var encodeString = function(s){
12706         if (/["\\\x00-\x1f]/.test(s)) {
12707             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12708                 var c = m[b];
12709                 if(c){
12710                     return c;
12711                 }
12712                 c = b.charCodeAt();
12713                 return "\\u00" +
12714                     Math.floor(c / 16).toString(16) +
12715                     (c % 16).toString(16);
12716             }) + '"';
12717         }
12718         return '"' + s + '"';
12719     };
12720     
12721     var encodeArray = function(o){
12722         var a = ["["], b, i, l = o.length, v;
12723             for (i = 0; i < l; i += 1) {
12724                 v = o[i];
12725                 switch (typeof v) {
12726                     case "undefined":
12727                     case "function":
12728                     case "unknown":
12729                         break;
12730                     default:
12731                         if (b) {
12732                             a.push(',');
12733                         }
12734                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12735                         b = true;
12736                 }
12737             }
12738             a.push("]");
12739             return a.join("");
12740     };
12741     
12742     var encodeDate = function(o){
12743         return '"' + o.getFullYear() + "-" +
12744                 pad(o.getMonth() + 1) + "-" +
12745                 pad(o.getDate()) + "T" +
12746                 pad(o.getHours()) + ":" +
12747                 pad(o.getMinutes()) + ":" +
12748                 pad(o.getSeconds()) + '"';
12749     };
12750     
12751     /**
12752      * Encodes an Object, Array or other value
12753      * @param {Mixed} o The variable to encode
12754      * @return {String} The JSON string
12755      */
12756     this.encode = function(o){
12757         if(typeof o == "undefined" || o === null){
12758             return "null";
12759         }else if(o instanceof Array){
12760             return encodeArray(o);
12761         }else if(o instanceof Date){
12762             return encodeDate(o);
12763         }else if(typeof o == "string"){
12764             return encodeString(o);
12765         }else if(typeof o == "number"){
12766             return isFinite(o) ? String(o) : "null";
12767         }else if(typeof o == "boolean"){
12768             return String(o);
12769         }else {
12770             var a = ["{"], b, i, v;
12771             for (i in o) {
12772                 if(!useHasOwn || o.hasOwnProperty(i)) {
12773                     v = o[i];
12774                     switch (typeof v) {
12775                     case "undefined":
12776                     case "function":
12777                     case "unknown":
12778                         break;
12779                     default:
12780                         if(b){
12781                             a.push(',');
12782                         }
12783                         a.push(this.encode(i), ":",
12784                                 v === null ? "null" : this.encode(v));
12785                         b = true;
12786                     }
12787                 }
12788             }
12789             a.push("}");
12790             return a.join("");
12791         }
12792     };
12793     
12794     /**
12795      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12796      * @param {String} json The JSON string
12797      * @return {Object} The resulting object
12798      */
12799     this.decode = function(json){
12800         /**
12801          * eval:var:json
12802          */
12803         return eval("(" + json + ')');
12804     };
12805 })();
12806 /** 
12807  * Shorthand for {@link Roo.util.JSON#encode}
12808  * @member Roo encode 
12809  * @method */
12810 Roo.encode = Roo.util.JSON.encode;
12811 /** 
12812  * Shorthand for {@link Roo.util.JSON#decode}
12813  * @member Roo decode 
12814  * @method */
12815 Roo.decode = Roo.util.JSON.decode;
12816 /*
12817  * Based on:
12818  * Ext JS Library 1.1.1
12819  * Copyright(c) 2006-2007, Ext JS, LLC.
12820  *
12821  * Originally Released Under LGPL - original licence link has changed is not relivant.
12822  *
12823  * Fork - LGPL
12824  * <script type="text/javascript">
12825  */
12826  
12827 /**
12828  * @class Roo.util.Format
12829  * Reusable data formatting functions
12830  * @singleton
12831  */
12832 Roo.util.Format = function(){
12833     var trimRe = /^\s+|\s+$/g;
12834     return {
12835         /**
12836          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12837          * @param {String} value The string to truncate
12838          * @param {Number} length The maximum length to allow before truncating
12839          * @return {String} The converted text
12840          */
12841         ellipsis : function(value, len){
12842             if(value && value.length > len){
12843                 return value.substr(0, len-3)+"...";
12844             }
12845             return value;
12846         },
12847
12848         /**
12849          * Checks a reference and converts it to empty string if it is undefined
12850          * @param {Mixed} value Reference to check
12851          * @return {Mixed} Empty string if converted, otherwise the original value
12852          */
12853         undef : function(value){
12854             return typeof value != "undefined" ? value : "";
12855         },
12856
12857         /**
12858          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12859          * @param {String} value The string to encode
12860          * @return {String} The encoded text
12861          */
12862         htmlEncode : function(value){
12863             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12864         },
12865
12866         /**
12867          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12868          * @param {String} value The string to decode
12869          * @return {String} The decoded text
12870          */
12871         htmlDecode : function(value){
12872             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12873         },
12874
12875         /**
12876          * Trims any whitespace from either side of a string
12877          * @param {String} value The text to trim
12878          * @return {String} The trimmed text
12879          */
12880         trim : function(value){
12881             return String(value).replace(trimRe, "");
12882         },
12883
12884         /**
12885          * Returns a substring from within an original string
12886          * @param {String} value The original text
12887          * @param {Number} start The start index of the substring
12888          * @param {Number} length The length of the substring
12889          * @return {String} The substring
12890          */
12891         substr : function(value, start, length){
12892             return String(value).substr(start, length);
12893         },
12894
12895         /**
12896          * Converts a string to all lower case letters
12897          * @param {String} value The text to convert
12898          * @return {String} The converted text
12899          */
12900         lowercase : function(value){
12901             return String(value).toLowerCase();
12902         },
12903
12904         /**
12905          * Converts a string to all upper case letters
12906          * @param {String} value The text to convert
12907          * @return {String} The converted text
12908          */
12909         uppercase : function(value){
12910             return String(value).toUpperCase();
12911         },
12912
12913         /**
12914          * Converts the first character only of a string to upper case
12915          * @param {String} value The text to convert
12916          * @return {String} The converted text
12917          */
12918         capitalize : function(value){
12919             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12920         },
12921
12922         // private
12923         call : function(value, fn){
12924             if(arguments.length > 2){
12925                 var args = Array.prototype.slice.call(arguments, 2);
12926                 args.unshift(value);
12927                  
12928                 return /** eval:var:value */  eval(fn).apply(window, args);
12929             }else{
12930                 /** eval:var:value */
12931                 return /** eval:var:value */ eval(fn).call(window, value);
12932             }
12933         },
12934
12935         /**
12936          * Format a number as US currency
12937          * @param {Number/String} value The numeric value to format
12938          * @return {String} The formatted currency string
12939          */
12940         usMoney : function(v){
12941             v = (Math.round((v-0)*100))/100;
12942             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12943             v = String(v);
12944             var ps = v.split('.');
12945             var whole = ps[0];
12946             var sub = ps[1] ? '.'+ ps[1] : '.00';
12947             var r = /(\d+)(\d{3})/;
12948             while (r.test(whole)) {
12949                 whole = whole.replace(r, '$1' + ',' + '$2');
12950             }
12951             return "$" + whole + sub ;
12952         },
12953
12954         /**
12955          * Parse a value into a formatted date using the specified format pattern.
12956          * @param {Mixed} value The value to format
12957          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12958          * @return {String} The formatted date string
12959          */
12960         date : function(v, format){
12961             if(!v){
12962                 return "";
12963             }
12964             if(!(v instanceof Date)){
12965                 v = new Date(Date.parse(v));
12966             }
12967             return v.dateFormat(format || "m/d/Y");
12968         },
12969
12970         /**
12971          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12972          * @param {String} format Any valid date format string
12973          * @return {Function} The date formatting function
12974          */
12975         dateRenderer : function(format){
12976             return function(v){
12977                 return Roo.util.Format.date(v, format);  
12978             };
12979         },
12980
12981         // private
12982         stripTagsRE : /<\/?[^>]+>/gi,
12983         
12984         /**
12985          * Strips all HTML tags
12986          * @param {Mixed} value The text from which to strip tags
12987          * @return {String} The stripped text
12988          */
12989         stripTags : function(v){
12990             return !v ? v : String(v).replace(this.stripTagsRE, "");
12991         }
12992     };
12993 }();/*
12994  * Based on:
12995  * Ext JS Library 1.1.1
12996  * Copyright(c) 2006-2007, Ext JS, LLC.
12997  *
12998  * Originally Released Under LGPL - original licence link has changed is not relivant.
12999  *
13000  * Fork - LGPL
13001  * <script type="text/javascript">
13002  */
13003
13004
13005  
13006
13007 /**
13008  * @class Roo.MasterTemplate
13009  * @extends Roo.Template
13010  * Provides a template that can have child templates. The syntax is:
13011 <pre><code>
13012 var t = new Roo.MasterTemplate(
13013         '&lt;select name="{name}"&gt;',
13014                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13015         '&lt;/select&gt;'
13016 );
13017 t.add('options', {value: 'foo', text: 'bar'});
13018 // or you can add multiple child elements in one shot
13019 t.addAll('options', [
13020     {value: 'foo', text: 'bar'},
13021     {value: 'foo2', text: 'bar2'},
13022     {value: 'foo3', text: 'bar3'}
13023 ]);
13024 // then append, applying the master template values
13025 t.append('my-form', {name: 'my-select'});
13026 </code></pre>
13027 * A name attribute for the child template is not required if you have only one child
13028 * template or you want to refer to them by index.
13029  */
13030 Roo.MasterTemplate = function(){
13031     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13032     this.originalHtml = this.html;
13033     var st = {};
13034     var m, re = this.subTemplateRe;
13035     re.lastIndex = 0;
13036     var subIndex = 0;
13037     while(m = re.exec(this.html)){
13038         var name = m[1], content = m[2];
13039         st[subIndex] = {
13040             name: name,
13041             index: subIndex,
13042             buffer: [],
13043             tpl : new Roo.Template(content)
13044         };
13045         if(name){
13046             st[name] = st[subIndex];
13047         }
13048         st[subIndex].tpl.compile();
13049         st[subIndex].tpl.call = this.call.createDelegate(this);
13050         subIndex++;
13051     }
13052     this.subCount = subIndex;
13053     this.subs = st;
13054 };
13055 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13056     /**
13057     * The regular expression used to match sub templates
13058     * @type RegExp
13059     * @property
13060     */
13061     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13062
13063     /**
13064      * Applies the passed values to a child template.
13065      * @param {String/Number} name (optional) The name or index of the child template
13066      * @param {Array/Object} values The values to be applied to the template
13067      * @return {MasterTemplate} this
13068      */
13069      add : function(name, values){
13070         if(arguments.length == 1){
13071             values = arguments[0];
13072             name = 0;
13073         }
13074         var s = this.subs[name];
13075         s.buffer[s.buffer.length] = s.tpl.apply(values);
13076         return this;
13077     },
13078
13079     /**
13080      * Applies all the passed values to a child template.
13081      * @param {String/Number} name (optional) The name or index of the child template
13082      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13083      * @param {Boolean} reset (optional) True to reset the template first
13084      * @return {MasterTemplate} this
13085      */
13086     fill : function(name, values, reset){
13087         var a = arguments;
13088         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13089             values = a[0];
13090             name = 0;
13091             reset = a[1];
13092         }
13093         if(reset){
13094             this.reset();
13095         }
13096         for(var i = 0, len = values.length; i < len; i++){
13097             this.add(name, values[i]);
13098         }
13099         return this;
13100     },
13101
13102     /**
13103      * Resets the template for reuse
13104      * @return {MasterTemplate} this
13105      */
13106      reset : function(){
13107         var s = this.subs;
13108         for(var i = 0; i < this.subCount; i++){
13109             s[i].buffer = [];
13110         }
13111         return this;
13112     },
13113
13114     applyTemplate : function(values){
13115         var s = this.subs;
13116         var replaceIndex = -1;
13117         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13118             return s[++replaceIndex].buffer.join("");
13119         });
13120         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13121     },
13122
13123     apply : function(){
13124         return this.applyTemplate.apply(this, arguments);
13125     },
13126
13127     compile : function(){return this;}
13128 });
13129
13130 /**
13131  * Alias for fill().
13132  * @method
13133  */
13134 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13135  /**
13136  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13137  * var tpl = Roo.MasterTemplate.from('element-id');
13138  * @param {String/HTMLElement} el
13139  * @param {Object} config
13140  * @static
13141  */
13142 Roo.MasterTemplate.from = function(el, config){
13143     el = Roo.getDom(el);
13144     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13145 };/*
13146  * Based on:
13147  * Ext JS Library 1.1.1
13148  * Copyright(c) 2006-2007, Ext JS, LLC.
13149  *
13150  * Originally Released Under LGPL - original licence link has changed is not relivant.
13151  *
13152  * Fork - LGPL
13153  * <script type="text/javascript">
13154  */
13155
13156  
13157 /**
13158  * @class Roo.util.CSS
13159  * Utility class for manipulating CSS rules
13160  * @singleton
13161  */
13162 Roo.util.CSS = function(){
13163         var rules = null;
13164         var doc = document;
13165
13166     var camelRe = /(-[a-z])/gi;
13167     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13168
13169    return {
13170    /**
13171     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13172     * tag and appended to the HEAD of the document.
13173     * @param {String} cssText The text containing the css rules
13174     * @param {String} id An id to add to the stylesheet for later removal
13175     * @return {StyleSheet}
13176     */
13177    createStyleSheet : function(cssText, id){
13178        var ss;
13179        var head = doc.getElementsByTagName("head")[0];
13180        var rules = doc.createElement("style");
13181        rules.setAttribute("type", "text/css");
13182        if(id){
13183            rules.setAttribute("id", id);
13184        }
13185        if(Roo.isIE){
13186            head.appendChild(rules);
13187            ss = rules.styleSheet;
13188            ss.cssText = cssText;
13189        }else{
13190            try{
13191                 rules.appendChild(doc.createTextNode(cssText));
13192            }catch(e){
13193                rules.cssText = cssText; 
13194            }
13195            head.appendChild(rules);
13196            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13197        }
13198        this.cacheStyleSheet(ss);
13199        return ss;
13200    },
13201
13202    /**
13203     * Removes a style or link tag by id
13204     * @param {String} id The id of the tag
13205     */
13206    removeStyleSheet : function(id){
13207        var existing = doc.getElementById(id);
13208        if(existing){
13209            existing.parentNode.removeChild(existing);
13210        }
13211    },
13212
13213    /**
13214     * Dynamically swaps an existing stylesheet reference for a new one
13215     * @param {String} id The id of an existing link tag to remove
13216     * @param {String} url The href of the new stylesheet to include
13217     */
13218    swapStyleSheet : function(id, url){
13219        this.removeStyleSheet(id);
13220        var ss = doc.createElement("link");
13221        ss.setAttribute("rel", "stylesheet");
13222        ss.setAttribute("type", "text/css");
13223        ss.setAttribute("id", id);
13224        ss.setAttribute("href", url);
13225        doc.getElementsByTagName("head")[0].appendChild(ss);
13226    },
13227    
13228    /**
13229     * Refresh the rule cache if you have dynamically added stylesheets
13230     * @return {Object} An object (hash) of rules indexed by selector
13231     */
13232    refreshCache : function(){
13233        return this.getRules(true);
13234    },
13235
13236    // private
13237    cacheStyleSheet : function(ss){
13238        if(!rules){
13239            rules = {};
13240        }
13241        try{// try catch for cross domain access issue
13242            var ssRules = ss.cssRules || ss.rules;
13243            for(var j = ssRules.length-1; j >= 0; --j){
13244                rules[ssRules[j].selectorText] = ssRules[j];
13245            }
13246        }catch(e){}
13247    },
13248    
13249    /**
13250     * Gets all css rules for the document
13251     * @param {Boolean} refreshCache true to refresh the internal cache
13252     * @return {Object} An object (hash) of rules indexed by selector
13253     */
13254    getRules : function(refreshCache){
13255                 if(rules == null || refreshCache){
13256                         rules = {};
13257                         var ds = doc.styleSheets;
13258                         for(var i =0, len = ds.length; i < len; i++){
13259                             try{
13260                         this.cacheStyleSheet(ds[i]);
13261                     }catch(e){} 
13262                 }
13263                 }
13264                 return rules;
13265         },
13266         
13267         /**
13268     * Gets an an individual CSS rule by selector(s)
13269     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13270     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13271     * @return {CSSRule} The CSS rule or null if one is not found
13272     */
13273    getRule : function(selector, refreshCache){
13274                 var rs = this.getRules(refreshCache);
13275                 if(!(selector instanceof Array)){
13276                     return rs[selector];
13277                 }
13278                 for(var i = 0; i < selector.length; i++){
13279                         if(rs[selector[i]]){
13280                                 return rs[selector[i]];
13281                         }
13282                 }
13283                 return null;
13284         },
13285         
13286         
13287         /**
13288     * Updates a rule property
13289     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13290     * @param {String} property The css property
13291     * @param {String} value The new value for the property
13292     * @return {Boolean} true If a rule was found and updated
13293     */
13294    updateRule : function(selector, property, value){
13295                 if(!(selector instanceof Array)){
13296                         var rule = this.getRule(selector);
13297                         if(rule){
13298                                 rule.style[property.replace(camelRe, camelFn)] = value;
13299                                 return true;
13300                         }
13301                 }else{
13302                         for(var i = 0; i < selector.length; i++){
13303                                 if(this.updateRule(selector[i], property, value)){
13304                                         return true;
13305                                 }
13306                         }
13307                 }
13308                 return false;
13309         }
13310    };   
13311 }();/*
13312  * Based on:
13313  * Ext JS Library 1.1.1
13314  * Copyright(c) 2006-2007, Ext JS, LLC.
13315  *
13316  * Originally Released Under LGPL - original licence link has changed is not relivant.
13317  *
13318  * Fork - LGPL
13319  * <script type="text/javascript">
13320  */
13321
13322  
13323
13324 /**
13325  * @class Roo.util.ClickRepeater
13326  * @extends Roo.util.Observable
13327  * 
13328  * A wrapper class which can be applied to any element. Fires a "click" event while the
13329  * mouse is pressed. The interval between firings may be specified in the config but
13330  * defaults to 10 milliseconds.
13331  * 
13332  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13333  * 
13334  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13335  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13336  * Similar to an autorepeat key delay.
13337  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13338  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13339  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13340  *           "interval" and "delay" are ignored. "immediate" is honored.
13341  * @cfg {Boolean} preventDefault True to prevent the default click event
13342  * @cfg {Boolean} stopDefault True to stop the default click event
13343  * 
13344  * @history
13345  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13346  *     2007-02-02 jvs Renamed to ClickRepeater
13347  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13348  *
13349  *  @constructor
13350  * @param {String/HTMLElement/Element} el The element to listen on
13351  * @param {Object} config
13352  **/
13353 Roo.util.ClickRepeater = function(el, config)
13354 {
13355     this.el = Roo.get(el);
13356     this.el.unselectable();
13357
13358     Roo.apply(this, config);
13359
13360     this.addEvents({
13361     /**
13362      * @event mousedown
13363      * Fires when the mouse button is depressed.
13364      * @param {Roo.util.ClickRepeater} this
13365      */
13366         "mousedown" : true,
13367     /**
13368      * @event click
13369      * Fires on a specified interval during the time the element is pressed.
13370      * @param {Roo.util.ClickRepeater} this
13371      */
13372         "click" : true,
13373     /**
13374      * @event mouseup
13375      * Fires when the mouse key is released.
13376      * @param {Roo.util.ClickRepeater} this
13377      */
13378         "mouseup" : true
13379     });
13380
13381     this.el.on("mousedown", this.handleMouseDown, this);
13382     if(this.preventDefault || this.stopDefault){
13383         this.el.on("click", function(e){
13384             if(this.preventDefault){
13385                 e.preventDefault();
13386             }
13387             if(this.stopDefault){
13388                 e.stopEvent();
13389             }
13390         }, this);
13391     }
13392
13393     // allow inline handler
13394     if(this.handler){
13395         this.on("click", this.handler,  this.scope || this);
13396     }
13397
13398     Roo.util.ClickRepeater.superclass.constructor.call(this);
13399 };
13400
13401 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13402     interval : 20,
13403     delay: 250,
13404     preventDefault : true,
13405     stopDefault : false,
13406     timer : 0,
13407
13408     // private
13409     handleMouseDown : function(){
13410         clearTimeout(this.timer);
13411         this.el.blur();
13412         if(this.pressClass){
13413             this.el.addClass(this.pressClass);
13414         }
13415         this.mousedownTime = new Date();
13416
13417         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13418         this.el.on("mouseout", this.handleMouseOut, this);
13419
13420         this.fireEvent("mousedown", this);
13421         this.fireEvent("click", this);
13422         
13423         this.timer = this.click.defer(this.delay || this.interval, this);
13424     },
13425
13426     // private
13427     click : function(){
13428         this.fireEvent("click", this);
13429         this.timer = this.click.defer(this.getInterval(), this);
13430     },
13431
13432     // private
13433     getInterval: function(){
13434         if(!this.accelerate){
13435             return this.interval;
13436         }
13437         var pressTime = this.mousedownTime.getElapsed();
13438         if(pressTime < 500){
13439             return 400;
13440         }else if(pressTime < 1700){
13441             return 320;
13442         }else if(pressTime < 2600){
13443             return 250;
13444         }else if(pressTime < 3500){
13445             return 180;
13446         }else if(pressTime < 4400){
13447             return 140;
13448         }else if(pressTime < 5300){
13449             return 80;
13450         }else if(pressTime < 6200){
13451             return 50;
13452         }else{
13453             return 10;
13454         }
13455     },
13456
13457     // private
13458     handleMouseOut : function(){
13459         clearTimeout(this.timer);
13460         if(this.pressClass){
13461             this.el.removeClass(this.pressClass);
13462         }
13463         this.el.on("mouseover", this.handleMouseReturn, this);
13464     },
13465
13466     // private
13467     handleMouseReturn : function(){
13468         this.el.un("mouseover", this.handleMouseReturn);
13469         if(this.pressClass){
13470             this.el.addClass(this.pressClass);
13471         }
13472         this.click();
13473     },
13474
13475     // private
13476     handleMouseUp : function(){
13477         clearTimeout(this.timer);
13478         this.el.un("mouseover", this.handleMouseReturn);
13479         this.el.un("mouseout", this.handleMouseOut);
13480         Roo.get(document).un("mouseup", this.handleMouseUp);
13481         this.el.removeClass(this.pressClass);
13482         this.fireEvent("mouseup", this);
13483     }
13484 });/*
13485  * Based on:
13486  * Ext JS Library 1.1.1
13487  * Copyright(c) 2006-2007, Ext JS, LLC.
13488  *
13489  * Originally Released Under LGPL - original licence link has changed is not relivant.
13490  *
13491  * Fork - LGPL
13492  * <script type="text/javascript">
13493  */
13494
13495  
13496 /**
13497  * @class Roo.KeyNav
13498  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13499  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13500  * way to implement custom navigation schemes for any UI component.</p>
13501  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13502  * pageUp, pageDown, del, home, end.  Usage:</p>
13503  <pre><code>
13504 var nav = new Roo.KeyNav("my-element", {
13505     "left" : function(e){
13506         this.moveLeft(e.ctrlKey);
13507     },
13508     "right" : function(e){
13509         this.moveRight(e.ctrlKey);
13510     },
13511     "enter" : function(e){
13512         this.save();
13513     },
13514     scope : this
13515 });
13516 </code></pre>
13517  * @constructor
13518  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13519  * @param {Object} config The config
13520  */
13521 Roo.KeyNav = function(el, config){
13522     this.el = Roo.get(el);
13523     Roo.apply(this, config);
13524     if(!this.disabled){
13525         this.disabled = true;
13526         this.enable();
13527     }
13528 };
13529
13530 Roo.KeyNav.prototype = {
13531     /**
13532      * @cfg {Boolean} disabled
13533      * True to disable this KeyNav instance (defaults to false)
13534      */
13535     disabled : false,
13536     /**
13537      * @cfg {String} defaultEventAction
13538      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13539      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13540      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13541      */
13542     defaultEventAction: "stopEvent",
13543     /**
13544      * @cfg {Boolean} forceKeyDown
13545      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13546      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13547      * handle keydown instead of keypress.
13548      */
13549     forceKeyDown : false,
13550
13551     // private
13552     prepareEvent : function(e){
13553         var k = e.getKey();
13554         var h = this.keyToHandler[k];
13555         //if(h && this[h]){
13556         //    e.stopPropagation();
13557         //}
13558         if(Roo.isSafari && h && k >= 37 && k <= 40){
13559             e.stopEvent();
13560         }
13561     },
13562
13563     // private
13564     relay : function(e){
13565         var k = e.getKey();
13566         var h = this.keyToHandler[k];
13567         if(h && this[h]){
13568             if(this.doRelay(e, this[h], h) !== true){
13569                 e[this.defaultEventAction]();
13570             }
13571         }
13572     },
13573
13574     // private
13575     doRelay : function(e, h, hname){
13576         return h.call(this.scope || this, e);
13577     },
13578
13579     // possible handlers
13580     enter : false,
13581     left : false,
13582     right : false,
13583     up : false,
13584     down : false,
13585     tab : false,
13586     esc : false,
13587     pageUp : false,
13588     pageDown : false,
13589     del : false,
13590     home : false,
13591     end : false,
13592
13593     // quick lookup hash
13594     keyToHandler : {
13595         37 : "left",
13596         39 : "right",
13597         38 : "up",
13598         40 : "down",
13599         33 : "pageUp",
13600         34 : "pageDown",
13601         46 : "del",
13602         36 : "home",
13603         35 : "end",
13604         13 : "enter",
13605         27 : "esc",
13606         9  : "tab"
13607     },
13608
13609         /**
13610          * Enable this KeyNav
13611          */
13612         enable: function(){
13613                 if(this.disabled){
13614             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13615             // the EventObject will normalize Safari automatically
13616             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13617                 this.el.on("keydown", this.relay,  this);
13618             }else{
13619                 this.el.on("keydown", this.prepareEvent,  this);
13620                 this.el.on("keypress", this.relay,  this);
13621             }
13622                     this.disabled = false;
13623                 }
13624         },
13625
13626         /**
13627          * Disable this KeyNav
13628          */
13629         disable: function(){
13630                 if(!this.disabled){
13631                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13632                 this.el.un("keydown", this.relay);
13633             }else{
13634                 this.el.un("keydown", this.prepareEvent);
13635                 this.el.un("keypress", this.relay);
13636             }
13637                     this.disabled = true;
13638                 }
13639         }
13640 };/*
13641  * Based on:
13642  * Ext JS Library 1.1.1
13643  * Copyright(c) 2006-2007, Ext JS, LLC.
13644  *
13645  * Originally Released Under LGPL - original licence link has changed is not relivant.
13646  *
13647  * Fork - LGPL
13648  * <script type="text/javascript">
13649  */
13650
13651  
13652 /**
13653  * @class Roo.KeyMap
13654  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13655  * The constructor accepts the same config object as defined by {@link #addBinding}.
13656  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13657  * combination it will call the function with this signature (if the match is a multi-key
13658  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13659  * A KeyMap can also handle a string representation of keys.<br />
13660  * Usage:
13661  <pre><code>
13662 // map one key by key code
13663 var map = new Roo.KeyMap("my-element", {
13664     key: 13, // or Roo.EventObject.ENTER
13665     fn: myHandler,
13666     scope: myObject
13667 });
13668
13669 // map multiple keys to one action by string
13670 var map = new Roo.KeyMap("my-element", {
13671     key: "a\r\n\t",
13672     fn: myHandler,
13673     scope: myObject
13674 });
13675
13676 // map multiple keys to multiple actions by strings and array of codes
13677 var map = new Roo.KeyMap("my-element", [
13678     {
13679         key: [10,13],
13680         fn: function(){ alert("Return was pressed"); }
13681     }, {
13682         key: "abc",
13683         fn: function(){ alert('a, b or c was pressed'); }
13684     }, {
13685         key: "\t",
13686         ctrl:true,
13687         shift:true,
13688         fn: function(){ alert('Control + shift + tab was pressed.'); }
13689     }
13690 ]);
13691 </code></pre>
13692  * <b>Note: A KeyMap starts enabled</b>
13693  * @constructor
13694  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13695  * @param {Object} config The config (see {@link #addBinding})
13696  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13697  */
13698 Roo.KeyMap = function(el, config, eventName){
13699     this.el  = Roo.get(el);
13700     this.eventName = eventName || "keydown";
13701     this.bindings = [];
13702     if(config){
13703         this.addBinding(config);
13704     }
13705     this.enable();
13706 };
13707
13708 Roo.KeyMap.prototype = {
13709     /**
13710      * True to stop the event from bubbling and prevent the default browser action if the
13711      * key was handled by the KeyMap (defaults to false)
13712      * @type Boolean
13713      */
13714     stopEvent : false,
13715
13716     /**
13717      * Add a new binding to this KeyMap. The following config object properties are supported:
13718      * <pre>
13719 Property    Type             Description
13720 ----------  ---------------  ----------------------------------------------------------------------
13721 key         String/Array     A single keycode or an array of keycodes to handle
13722 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13723 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13724 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13725 fn          Function         The function to call when KeyMap finds the expected key combination
13726 scope       Object           The scope of the callback function
13727 </pre>
13728      *
13729      * Usage:
13730      * <pre><code>
13731 // Create a KeyMap
13732 var map = new Roo.KeyMap(document, {
13733     key: Roo.EventObject.ENTER,
13734     fn: handleKey,
13735     scope: this
13736 });
13737
13738 //Add a new binding to the existing KeyMap later
13739 map.addBinding({
13740     key: 'abc',
13741     shift: true,
13742     fn: handleKey,
13743     scope: this
13744 });
13745 </code></pre>
13746      * @param {Object/Array} config A single KeyMap config or an array of configs
13747      */
13748         addBinding : function(config){
13749         if(config instanceof Array){
13750             for(var i = 0, len = config.length; i < len; i++){
13751                 this.addBinding(config[i]);
13752             }
13753             return;
13754         }
13755         var keyCode = config.key,
13756             shift = config.shift, 
13757             ctrl = config.ctrl, 
13758             alt = config.alt,
13759             fn = config.fn,
13760             scope = config.scope;
13761         if(typeof keyCode == "string"){
13762             var ks = [];
13763             var keyString = keyCode.toUpperCase();
13764             for(var j = 0, len = keyString.length; j < len; j++){
13765                 ks.push(keyString.charCodeAt(j));
13766             }
13767             keyCode = ks;
13768         }
13769         var keyArray = keyCode instanceof Array;
13770         var handler = function(e){
13771             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13772                 var k = e.getKey();
13773                 if(keyArray){
13774                     for(var i = 0, len = keyCode.length; i < len; i++){
13775                         if(keyCode[i] == k){
13776                           if(this.stopEvent){
13777                               e.stopEvent();
13778                           }
13779                           fn.call(scope || window, k, e);
13780                           return;
13781                         }
13782                     }
13783                 }else{
13784                     if(k == keyCode){
13785                         if(this.stopEvent){
13786                            e.stopEvent();
13787                         }
13788                         fn.call(scope || window, k, e);
13789                     }
13790                 }
13791             }
13792         };
13793         this.bindings.push(handler);  
13794         },
13795
13796     /**
13797      * Shorthand for adding a single key listener
13798      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13799      * following options:
13800      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13801      * @param {Function} fn The function to call
13802      * @param {Object} scope (optional) The scope of the function
13803      */
13804     on : function(key, fn, scope){
13805         var keyCode, shift, ctrl, alt;
13806         if(typeof key == "object" && !(key instanceof Array)){
13807             keyCode = key.key;
13808             shift = key.shift;
13809             ctrl = key.ctrl;
13810             alt = key.alt;
13811         }else{
13812             keyCode = key;
13813         }
13814         this.addBinding({
13815             key: keyCode,
13816             shift: shift,
13817             ctrl: ctrl,
13818             alt: alt,
13819             fn: fn,
13820             scope: scope
13821         })
13822     },
13823
13824     // private
13825     handleKeyDown : function(e){
13826             if(this.enabled){ //just in case
13827             var b = this.bindings;
13828             for(var i = 0, len = b.length; i < len; i++){
13829                 b[i].call(this, e);
13830             }
13831             }
13832         },
13833         
13834         /**
13835          * Returns true if this KeyMap is enabled
13836          * @return {Boolean} 
13837          */
13838         isEnabled : function(){
13839             return this.enabled;  
13840         },
13841         
13842         /**
13843          * Enables this KeyMap
13844          */
13845         enable: function(){
13846                 if(!this.enabled){
13847                     this.el.on(this.eventName, this.handleKeyDown, this);
13848                     this.enabled = true;
13849                 }
13850         },
13851
13852         /**
13853          * Disable this KeyMap
13854          */
13855         disable: function(){
13856                 if(this.enabled){
13857                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13858                     this.enabled = false;
13859                 }
13860         }
13861 };/*
13862  * Based on:
13863  * Ext JS Library 1.1.1
13864  * Copyright(c) 2006-2007, Ext JS, LLC.
13865  *
13866  * Originally Released Under LGPL - original licence link has changed is not relivant.
13867  *
13868  * Fork - LGPL
13869  * <script type="text/javascript">
13870  */
13871
13872  
13873 /**
13874  * @class Roo.util.TextMetrics
13875  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13876  * wide, in pixels, a given block of text will be.
13877  * @singleton
13878  */
13879 Roo.util.TextMetrics = function(){
13880     var shared;
13881     return {
13882         /**
13883          * Measures the size of the specified text
13884          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13885          * that can affect the size of the rendered text
13886          * @param {String} text The text to measure
13887          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13888          * in order to accurately measure the text height
13889          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13890          */
13891         measure : function(el, text, fixedWidth){
13892             if(!shared){
13893                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13894             }
13895             shared.bind(el);
13896             shared.setFixedWidth(fixedWidth || 'auto');
13897             return shared.getSize(text);
13898         },
13899
13900         /**
13901          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13902          * the overhead of multiple calls to initialize the style properties on each measurement.
13903          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13904          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13905          * in order to accurately measure the text height
13906          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13907          */
13908         createInstance : function(el, fixedWidth){
13909             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13910         }
13911     };
13912 }();
13913
13914  
13915
13916 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13917     var ml = new Roo.Element(document.createElement('div'));
13918     document.body.appendChild(ml.dom);
13919     ml.position('absolute');
13920     ml.setLeftTop(-1000, -1000);
13921     ml.hide();
13922
13923     if(fixedWidth){
13924         ml.setWidth(fixedWidth);
13925     }
13926      
13927     var instance = {
13928         /**
13929          * Returns the size of the specified text based on the internal element's style and width properties
13930          * @memberOf Roo.util.TextMetrics.Instance#
13931          * @param {String} text The text to measure
13932          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13933          */
13934         getSize : function(text){
13935             ml.update(text);
13936             var s = ml.getSize();
13937             ml.update('');
13938             return s;
13939         },
13940
13941         /**
13942          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13943          * that can affect the size of the rendered text
13944          * @memberOf Roo.util.TextMetrics.Instance#
13945          * @param {String/HTMLElement} el The element, dom node or id
13946          */
13947         bind : function(el){
13948             ml.setStyle(
13949                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13950             );
13951         },
13952
13953         /**
13954          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13955          * to set a fixed width in order to accurately measure the text height.
13956          * @memberOf Roo.util.TextMetrics.Instance#
13957          * @param {Number} width The width to set on the element
13958          */
13959         setFixedWidth : function(width){
13960             ml.setWidth(width);
13961         },
13962
13963         /**
13964          * Returns the measured width of the specified text
13965          * @memberOf Roo.util.TextMetrics.Instance#
13966          * @param {String} text The text to measure
13967          * @return {Number} width The width in pixels
13968          */
13969         getWidth : function(text){
13970             ml.dom.style.width = 'auto';
13971             return this.getSize(text).width;
13972         },
13973
13974         /**
13975          * Returns the measured height of the specified text.  For multiline text, be sure to call
13976          * {@link #setFixedWidth} if necessary.
13977          * @memberOf Roo.util.TextMetrics.Instance#
13978          * @param {String} text The text to measure
13979          * @return {Number} height The height in pixels
13980          */
13981         getHeight : function(text){
13982             return this.getSize(text).height;
13983         }
13984     };
13985
13986     instance.bind(bindTo);
13987
13988     return instance;
13989 };
13990
13991 // backwards compat
13992 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13993  * Based on:
13994  * Ext JS Library 1.1.1
13995  * Copyright(c) 2006-2007, Ext JS, LLC.
13996  *
13997  * Originally Released Under LGPL - original licence link has changed is not relivant.
13998  *
13999  * Fork - LGPL
14000  * <script type="text/javascript">
14001  */
14002
14003 /**
14004  * @class Roo.state.Provider
14005  * Abstract base class for state provider implementations. This class provides methods
14006  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14007  * Provider interface.
14008  */
14009 Roo.state.Provider = function(){
14010     /**
14011      * @event statechange
14012      * Fires when a state change occurs.
14013      * @param {Provider} this This state provider
14014      * @param {String} key The state key which was changed
14015      * @param {String} value The encoded value for the state
14016      */
14017     this.addEvents({
14018         "statechange": true
14019     });
14020     this.state = {};
14021     Roo.state.Provider.superclass.constructor.call(this);
14022 };
14023 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14024     /**
14025      * Returns the current value for a key
14026      * @param {String} name The key name
14027      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14028      * @return {Mixed} The state data
14029      */
14030     get : function(name, defaultValue){
14031         return typeof this.state[name] == "undefined" ?
14032             defaultValue : this.state[name];
14033     },
14034     
14035     /**
14036      * Clears a value from the state
14037      * @param {String} name The key name
14038      */
14039     clear : function(name){
14040         delete this.state[name];
14041         this.fireEvent("statechange", this, name, null);
14042     },
14043     
14044     /**
14045      * Sets the value for a key
14046      * @param {String} name The key name
14047      * @param {Mixed} value The value to set
14048      */
14049     set : function(name, value){
14050         this.state[name] = value;
14051         this.fireEvent("statechange", this, name, value);
14052     },
14053     
14054     /**
14055      * Decodes a string previously encoded with {@link #encodeValue}.
14056      * @param {String} value The value to decode
14057      * @return {Mixed} The decoded value
14058      */
14059     decodeValue : function(cookie){
14060         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14061         var matches = re.exec(unescape(cookie));
14062         if(!matches || !matches[1]) return; // non state cookie
14063         var type = matches[1];
14064         var v = matches[2];
14065         switch(type){
14066             case "n":
14067                 return parseFloat(v);
14068             case "d":
14069                 return new Date(Date.parse(v));
14070             case "b":
14071                 return (v == "1");
14072             case "a":
14073                 var all = [];
14074                 var values = v.split("^");
14075                 for(var i = 0, len = values.length; i < len; i++){
14076                     all.push(this.decodeValue(values[i]));
14077                 }
14078                 return all;
14079            case "o":
14080                 var all = {};
14081                 var values = v.split("^");
14082                 for(var i = 0, len = values.length; i < len; i++){
14083                     var kv = values[i].split("=");
14084                     all[kv[0]] = this.decodeValue(kv[1]);
14085                 }
14086                 return all;
14087            default:
14088                 return v;
14089         }
14090     },
14091     
14092     /**
14093      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14094      * @param {Mixed} value The value to encode
14095      * @return {String} The encoded value
14096      */
14097     encodeValue : function(v){
14098         var enc;
14099         if(typeof v == "number"){
14100             enc = "n:" + v;
14101         }else if(typeof v == "boolean"){
14102             enc = "b:" + (v ? "1" : "0");
14103         }else if(v instanceof Date){
14104             enc = "d:" + v.toGMTString();
14105         }else if(v instanceof Array){
14106             var flat = "";
14107             for(var i = 0, len = v.length; i < len; i++){
14108                 flat += this.encodeValue(v[i]);
14109                 if(i != len-1) flat += "^";
14110             }
14111             enc = "a:" + flat;
14112         }else if(typeof v == "object"){
14113             var flat = "";
14114             for(var key in v){
14115                 if(typeof v[key] != "function"){
14116                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14117                 }
14118             }
14119             enc = "o:" + flat.substring(0, flat.length-1);
14120         }else{
14121             enc = "s:" + v;
14122         }
14123         return escape(enc);        
14124     }
14125 });
14126
14127 /*
14128  * Based on:
14129  * Ext JS Library 1.1.1
14130  * Copyright(c) 2006-2007, Ext JS, LLC.
14131  *
14132  * Originally Released Under LGPL - original licence link has changed is not relivant.
14133  *
14134  * Fork - LGPL
14135  * <script type="text/javascript">
14136  */
14137 /**
14138  * @class Roo.state.Manager
14139  * This is the global state manager. By default all components that are "state aware" check this class
14140  * for state information if you don't pass them a custom state provider. In order for this class
14141  * to be useful, it must be initialized with a provider when your application initializes.
14142  <pre><code>
14143 // in your initialization function
14144 init : function(){
14145    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14146    ...
14147    // supposed you have a {@link Roo.BorderLayout}
14148    var layout = new Roo.BorderLayout(...);
14149    layout.restoreState();
14150    // or a {Roo.BasicDialog}
14151    var dialog = new Roo.BasicDialog(...);
14152    dialog.restoreState();
14153  </code></pre>
14154  * @singleton
14155  */
14156 Roo.state.Manager = function(){
14157     var provider = new Roo.state.Provider();
14158     
14159     return {
14160         /**
14161          * Configures the default state provider for your application
14162          * @param {Provider} stateProvider The state provider to set
14163          */
14164         setProvider : function(stateProvider){
14165             provider = stateProvider;
14166         },
14167         
14168         /**
14169          * Returns the current value for a key
14170          * @param {String} name The key name
14171          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14172          * @return {Mixed} The state data
14173          */
14174         get : function(key, defaultValue){
14175             return provider.get(key, defaultValue);
14176         },
14177         
14178         /**
14179          * Sets the value for a key
14180          * @param {String} name The key name
14181          * @param {Mixed} value The state data
14182          */
14183          set : function(key, value){
14184             provider.set(key, value);
14185         },
14186         
14187         /**
14188          * Clears a value from the state
14189          * @param {String} name The key name
14190          */
14191         clear : function(key){
14192             provider.clear(key);
14193         },
14194         
14195         /**
14196          * Gets the currently configured state provider
14197          * @return {Provider} The state provider
14198          */
14199         getProvider : function(){
14200             return provider;
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.CookieProvider
14216  * @extends Roo.state.Provider
14217  * The default Provider implementation which saves state via cookies.
14218  * <br />Usage:
14219  <pre><code>
14220    var cp = new Roo.state.CookieProvider({
14221        path: "/cgi-bin/",
14222        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14223        domain: "roojs.com"
14224    })
14225    Roo.state.Manager.setProvider(cp);
14226  </code></pre>
14227  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14228  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14229  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14230  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14231  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14232  * domain the page is running on including the 'www' like 'www.roojs.com')
14233  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14234  * @constructor
14235  * Create a new CookieProvider
14236  * @param {Object} config The configuration object
14237  */
14238 Roo.state.CookieProvider = function(config){
14239     Roo.state.CookieProvider.superclass.constructor.call(this);
14240     this.path = "/";
14241     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14242     this.domain = null;
14243     this.secure = false;
14244     Roo.apply(this, config);
14245     this.state = this.readCookies();
14246 };
14247
14248 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14249     // private
14250     set : function(name, value){
14251         if(typeof value == "undefined" || value === null){
14252             this.clear(name);
14253             return;
14254         }
14255         this.setCookie(name, value);
14256         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14257     },
14258
14259     // private
14260     clear : function(name){
14261         this.clearCookie(name);
14262         Roo.state.CookieProvider.superclass.clear.call(this, name);
14263     },
14264
14265     // private
14266     readCookies : function(){
14267         var cookies = {};
14268         var c = document.cookie + ";";
14269         var re = /\s?(.*?)=(.*?);/g;
14270         var matches;
14271         while((matches = re.exec(c)) != null){
14272             var name = matches[1];
14273             var value = matches[2];
14274             if(name && name.substring(0,3) == "ys-"){
14275                 cookies[name.substr(3)] = this.decodeValue(value);
14276             }
14277         }
14278         return cookies;
14279     },
14280
14281     // private
14282     setCookie : function(name, value){
14283         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14284            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14285            ((this.path == null) ? "" : ("; path=" + this.path)) +
14286            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14287            ((this.secure == true) ? "; secure" : "");
14288     },
14289
14290     // private
14291     clearCookie : function(name){
14292         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14293            ((this.path == null) ? "" : ("; path=" + this.path)) +
14294            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14295            ((this.secure == true) ? "; secure" : "");
14296     }
14297 });/*
14298  * Based on:
14299  * Ext JS Library 1.1.1
14300  * Copyright(c) 2006-2007, Ext JS, LLC.
14301  *
14302  * Originally Released Under LGPL - original licence link has changed is not relivant.
14303  *
14304  * Fork - LGPL
14305  * <script type="text/javascript">
14306  */
14307
14308
14309
14310 /*
14311  * These classes are derivatives of the similarly named classes in the YUI Library.
14312  * The original license:
14313  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14314  * Code licensed under the BSD License:
14315  * http://developer.yahoo.net/yui/license.txt
14316  */
14317
14318 (function() {
14319
14320 var Event=Roo.EventManager;
14321 var Dom=Roo.lib.Dom;
14322
14323 /**
14324  * @class Roo.dd.DragDrop
14325  * Defines the interface and base operation of items that that can be
14326  * dragged or can be drop targets.  It was designed to be extended, overriding
14327  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14328  * Up to three html elements can be associated with a DragDrop instance:
14329  * <ul>
14330  * <li>linked element: the element that is passed into the constructor.
14331  * This is the element which defines the boundaries for interaction with
14332  * other DragDrop objects.</li>
14333  * <li>handle element(s): The drag operation only occurs if the element that
14334  * was clicked matches a handle element.  By default this is the linked
14335  * element, but there are times that you will want only a portion of the
14336  * linked element to initiate the drag operation, and the setHandleElId()
14337  * method provides a way to define this.</li>
14338  * <li>drag element: this represents the element that would be moved along
14339  * with the cursor during a drag operation.  By default, this is the linked
14340  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14341  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14342  * </li>
14343  * </ul>
14344  * This class should not be instantiated until the onload event to ensure that
14345  * the associated elements are available.
14346  * The following would define a DragDrop obj that would interact with any
14347  * other DragDrop obj in the "group1" group:
14348  * <pre>
14349  *  dd = new Roo.dd.DragDrop("div1", "group1");
14350  * </pre>
14351  * Since none of the event handlers have been implemented, nothing would
14352  * actually happen if you were to run the code above.  Normally you would
14353  * override this class or one of the default implementations, but you can
14354  * also override the methods you want on an instance of the class...
14355  * <pre>
14356  *  dd.onDragDrop = function(e, id) {
14357  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14358  *  }
14359  * </pre>
14360  * @constructor
14361  * @param {String} id of the element that is linked to this instance
14362  * @param {String} sGroup the group of related DragDrop objects
14363  * @param {object} config an object containing configurable attributes
14364  *                Valid properties for DragDrop:
14365  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14366  */
14367 Roo.dd.DragDrop = function(id, sGroup, config) {
14368     if (id) {
14369         this.init(id, sGroup, config);
14370     }
14371 };
14372
14373 Roo.dd.DragDrop.prototype = {
14374
14375     /**
14376      * The id of the element associated with this object.  This is what we
14377      * refer to as the "linked element" because the size and position of
14378      * this element is used to determine when the drag and drop objects have
14379      * interacted.
14380      * @property id
14381      * @type String
14382      */
14383     id: null,
14384
14385     /**
14386      * Configuration attributes passed into the constructor
14387      * @property config
14388      * @type object
14389      */
14390     config: null,
14391
14392     /**
14393      * The id of the element that will be dragged.  By default this is same
14394      * as the linked element , but could be changed to another element. Ex:
14395      * Roo.dd.DDProxy
14396      * @property dragElId
14397      * @type String
14398      * @private
14399      */
14400     dragElId: null,
14401
14402     /**
14403      * the id of the element that initiates the drag operation.  By default
14404      * this is the linked element, but could be changed to be a child of this
14405      * element.  This lets us do things like only starting the drag when the
14406      * header element within the linked html element is clicked.
14407      * @property handleElId
14408      * @type String
14409      * @private
14410      */
14411     handleElId: null,
14412
14413     /**
14414      * An associative array of HTML tags that will be ignored if clicked.
14415      * @property invalidHandleTypes
14416      * @type {string: string}
14417      */
14418     invalidHandleTypes: null,
14419
14420     /**
14421      * An associative array of ids for elements that will be ignored if clicked
14422      * @property invalidHandleIds
14423      * @type {string: string}
14424      */
14425     invalidHandleIds: null,
14426
14427     /**
14428      * An indexted array of css class names for elements that will be ignored
14429      * if clicked.
14430      * @property invalidHandleClasses
14431      * @type string[]
14432      */
14433     invalidHandleClasses: null,
14434
14435     /**
14436      * The linked element's absolute X position at the time the drag was
14437      * started
14438      * @property startPageX
14439      * @type int
14440      * @private
14441      */
14442     startPageX: 0,
14443
14444     /**
14445      * The linked element's absolute X position at the time the drag was
14446      * started
14447      * @property startPageY
14448      * @type int
14449      * @private
14450      */
14451     startPageY: 0,
14452
14453     /**
14454      * The group defines a logical collection of DragDrop objects that are
14455      * related.  Instances only get events when interacting with other
14456      * DragDrop object in the same group.  This lets us define multiple
14457      * groups using a single DragDrop subclass if we want.
14458      * @property groups
14459      * @type {string: string}
14460      */
14461     groups: null,
14462
14463     /**
14464      * Individual drag/drop instances can be locked.  This will prevent
14465      * onmousedown start drag.
14466      * @property locked
14467      * @type boolean
14468      * @private
14469      */
14470     locked: false,
14471
14472     /**
14473      * Lock this instance
14474      * @method lock
14475      */
14476     lock: function() { this.locked = true; },
14477
14478     /**
14479      * Unlock this instace
14480      * @method unlock
14481      */
14482     unlock: function() { this.locked = false; },
14483
14484     /**
14485      * By default, all insances can be a drop target.  This can be disabled by
14486      * setting isTarget to false.
14487      * @method isTarget
14488      * @type boolean
14489      */
14490     isTarget: true,
14491
14492     /**
14493      * The padding configured for this drag and drop object for calculating
14494      * the drop zone intersection with this object.
14495      * @method padding
14496      * @type int[]
14497      */
14498     padding: null,
14499
14500     /**
14501      * Cached reference to the linked element
14502      * @property _domRef
14503      * @private
14504      */
14505     _domRef: null,
14506
14507     /**
14508      * Internal typeof flag
14509      * @property __ygDragDrop
14510      * @private
14511      */
14512     __ygDragDrop: true,
14513
14514     /**
14515      * Set to true when horizontal contraints are applied
14516      * @property constrainX
14517      * @type boolean
14518      * @private
14519      */
14520     constrainX: false,
14521
14522     /**
14523      * Set to true when vertical contraints are applied
14524      * @property constrainY
14525      * @type boolean
14526      * @private
14527      */
14528     constrainY: false,
14529
14530     /**
14531      * The left constraint
14532      * @property minX
14533      * @type int
14534      * @private
14535      */
14536     minX: 0,
14537
14538     /**
14539      * The right constraint
14540      * @property maxX
14541      * @type int
14542      * @private
14543      */
14544     maxX: 0,
14545
14546     /**
14547      * The up constraint
14548      * @property minY
14549      * @type int
14550      * @type int
14551      * @private
14552      */
14553     minY: 0,
14554
14555     /**
14556      * The down constraint
14557      * @property maxY
14558      * @type int
14559      * @private
14560      */
14561     maxY: 0,
14562
14563     /**
14564      * Maintain offsets when we resetconstraints.  Set to true when you want
14565      * the position of the element relative to its parent to stay the same
14566      * when the page changes
14567      *
14568      * @property maintainOffset
14569      * @type boolean
14570      */
14571     maintainOffset: false,
14572
14573     /**
14574      * Array of pixel locations the element will snap to if we specified a
14575      * horizontal graduation/interval.  This array is generated automatically
14576      * when you define a tick interval.
14577      * @property xTicks
14578      * @type int[]
14579      */
14580     xTicks: null,
14581
14582     /**
14583      * Array of pixel locations the element will snap to if we specified a
14584      * vertical graduation/interval.  This array is generated automatically
14585      * when you define a tick interval.
14586      * @property yTicks
14587      * @type int[]
14588      */
14589     yTicks: null,
14590
14591     /**
14592      * By default the drag and drop instance will only respond to the primary
14593      * button click (left button for a right-handed mouse).  Set to true to
14594      * allow drag and drop to start with any mouse click that is propogated
14595      * by the browser
14596      * @property primaryButtonOnly
14597      * @type boolean
14598      */
14599     primaryButtonOnly: true,
14600
14601     /**
14602      * The availabe property is false until the linked dom element is accessible.
14603      * @property available
14604      * @type boolean
14605      */
14606     available: false,
14607
14608     /**
14609      * By default, drags can only be initiated if the mousedown occurs in the
14610      * region the linked element is.  This is done in part to work around a
14611      * bug in some browsers that mis-report the mousedown if the previous
14612      * mouseup happened outside of the window.  This property is set to true
14613      * if outer handles are defined.
14614      *
14615      * @property hasOuterHandles
14616      * @type boolean
14617      * @default false
14618      */
14619     hasOuterHandles: false,
14620
14621     /**
14622      * Code that executes immediately before the startDrag event
14623      * @method b4StartDrag
14624      * @private
14625      */
14626     b4StartDrag: function(x, y) { },
14627
14628     /**
14629      * Abstract method called after a drag/drop object is clicked
14630      * and the drag or mousedown time thresholds have beeen met.
14631      * @method startDrag
14632      * @param {int} X click location
14633      * @param {int} Y click location
14634      */
14635     startDrag: function(x, y) { /* override this */ },
14636
14637     /**
14638      * Code that executes immediately before the onDrag event
14639      * @method b4Drag
14640      * @private
14641      */
14642     b4Drag: function(e) { },
14643
14644     /**
14645      * Abstract method called during the onMouseMove event while dragging an
14646      * object.
14647      * @method onDrag
14648      * @param {Event} e the mousemove event
14649      */
14650     onDrag: function(e) { /* override this */ },
14651
14652     /**
14653      * Abstract method called when this element fist begins hovering over
14654      * another DragDrop obj
14655      * @method onDragEnter
14656      * @param {Event} e the mousemove event
14657      * @param {String|DragDrop[]} id In POINT mode, the element
14658      * id this is hovering over.  In INTERSECT mode, an array of one or more
14659      * dragdrop items being hovered over.
14660      */
14661     onDragEnter: function(e, id) { /* override this */ },
14662
14663     /**
14664      * Code that executes immediately before the onDragOver event
14665      * @method b4DragOver
14666      * @private
14667      */
14668     b4DragOver: function(e) { },
14669
14670     /**
14671      * Abstract method called when this element is hovering over another
14672      * DragDrop obj
14673      * @method onDragOver
14674      * @param {Event} e the mousemove event
14675      * @param {String|DragDrop[]} id In POINT mode, the element
14676      * id this is hovering over.  In INTERSECT mode, an array of dd items
14677      * being hovered over.
14678      */
14679     onDragOver: function(e, id) { /* override this */ },
14680
14681     /**
14682      * Code that executes immediately before the onDragOut event
14683      * @method b4DragOut
14684      * @private
14685      */
14686     b4DragOut: function(e) { },
14687
14688     /**
14689      * Abstract method called when we are no longer hovering over an element
14690      * @method onDragOut
14691      * @param {Event} e the mousemove event
14692      * @param {String|DragDrop[]} id In POINT mode, the element
14693      * id this was hovering over.  In INTERSECT mode, an array of dd items
14694      * that the mouse is no longer over.
14695      */
14696     onDragOut: function(e, id) { /* override this */ },
14697
14698     /**
14699      * Code that executes immediately before the onDragDrop event
14700      * @method b4DragDrop
14701      * @private
14702      */
14703     b4DragDrop: function(e) { },
14704
14705     /**
14706      * Abstract method called when this item is dropped on another DragDrop
14707      * obj
14708      * @method onDragDrop
14709      * @param {Event} e the mouseup event
14710      * @param {String|DragDrop[]} id In POINT mode, the element
14711      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14712      * was dropped on.
14713      */
14714     onDragDrop: function(e, id) { /* override this */ },
14715
14716     /**
14717      * Abstract method called when this item is dropped on an area with no
14718      * drop target
14719      * @method onInvalidDrop
14720      * @param {Event} e the mouseup event
14721      */
14722     onInvalidDrop: function(e) { /* override this */ },
14723
14724     /**
14725      * Code that executes immediately before the endDrag event
14726      * @method b4EndDrag
14727      * @private
14728      */
14729     b4EndDrag: function(e) { },
14730
14731     /**
14732      * Fired when we are done dragging the object
14733      * @method endDrag
14734      * @param {Event} e the mouseup event
14735      */
14736     endDrag: function(e) { /* override this */ },
14737
14738     /**
14739      * Code executed immediately before the onMouseDown event
14740      * @method b4MouseDown
14741      * @param {Event} e the mousedown event
14742      * @private
14743      */
14744     b4MouseDown: function(e) {  },
14745
14746     /**
14747      * Event handler that fires when a drag/drop obj gets a mousedown
14748      * @method onMouseDown
14749      * @param {Event} e the mousedown event
14750      */
14751     onMouseDown: function(e) { /* override this */ },
14752
14753     /**
14754      * Event handler that fires when a drag/drop obj gets a mouseup
14755      * @method onMouseUp
14756      * @param {Event} e the mouseup event
14757      */
14758     onMouseUp: function(e) { /* override this */ },
14759
14760     /**
14761      * Override the onAvailable method to do what is needed after the initial
14762      * position was determined.
14763      * @method onAvailable
14764      */
14765     onAvailable: function () {
14766     },
14767
14768     /*
14769      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14770      * @type Object
14771      */
14772     defaultPadding : {left:0, right:0, top:0, bottom:0},
14773
14774     /*
14775      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14776  *
14777  * Usage:
14778  <pre><code>
14779  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14780                 { dragElId: "existingProxyDiv" });
14781  dd.startDrag = function(){
14782      this.constrainTo("parent-id");
14783  };
14784  </code></pre>
14785  * Or you can initalize it using the {@link Roo.Element} object:
14786  <pre><code>
14787  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14788      startDrag : function(){
14789          this.constrainTo("parent-id");
14790      }
14791  });
14792  </code></pre>
14793      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14794      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14795      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14796      * an object containing the sides to pad. For example: {right:10, bottom:10}
14797      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14798      */
14799     constrainTo : function(constrainTo, pad, inContent){
14800         if(typeof pad == "number"){
14801             pad = {left: pad, right:pad, top:pad, bottom:pad};
14802         }
14803         pad = pad || this.defaultPadding;
14804         var b = Roo.get(this.getEl()).getBox();
14805         var ce = Roo.get(constrainTo);
14806         var s = ce.getScroll();
14807         var c, cd = ce.dom;
14808         if(cd == document.body){
14809             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14810         }else{
14811             xy = ce.getXY();
14812             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14813         }
14814
14815
14816         var topSpace = b.y - c.y;
14817         var leftSpace = b.x - c.x;
14818
14819         this.resetConstraints();
14820         this.setXConstraint(leftSpace - (pad.left||0), // left
14821                 c.width - leftSpace - b.width - (pad.right||0) //right
14822         );
14823         this.setYConstraint(topSpace - (pad.top||0), //top
14824                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14825         );
14826     },
14827
14828     /**
14829      * Returns a reference to the linked element
14830      * @method getEl
14831      * @return {HTMLElement} the html element
14832      */
14833     getEl: function() {
14834         if (!this._domRef) {
14835             this._domRef = Roo.getDom(this.id);
14836         }
14837
14838         return this._domRef;
14839     },
14840
14841     /**
14842      * Returns a reference to the actual element to drag.  By default this is
14843      * the same as the html element, but it can be assigned to another
14844      * element. An example of this can be found in Roo.dd.DDProxy
14845      * @method getDragEl
14846      * @return {HTMLElement} the html element
14847      */
14848     getDragEl: function() {
14849         return Roo.getDom(this.dragElId);
14850     },
14851
14852     /**
14853      * Sets up the DragDrop object.  Must be called in the constructor of any
14854      * Roo.dd.DragDrop subclass
14855      * @method init
14856      * @param id the id of the linked element
14857      * @param {String} sGroup the group of related items
14858      * @param {object} config configuration attributes
14859      */
14860     init: function(id, sGroup, config) {
14861         this.initTarget(id, sGroup, config);
14862         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14863         // Event.on(this.id, "selectstart", Event.preventDefault);
14864     },
14865
14866     /**
14867      * Initializes Targeting functionality only... the object does not
14868      * get a mousedown handler.
14869      * @method initTarget
14870      * @param id the id of the linked element
14871      * @param {String} sGroup the group of related items
14872      * @param {object} config configuration attributes
14873      */
14874     initTarget: function(id, sGroup, config) {
14875
14876         // configuration attributes
14877         this.config = config || {};
14878
14879         // create a local reference to the drag and drop manager
14880         this.DDM = Roo.dd.DDM;
14881         // initialize the groups array
14882         this.groups = {};
14883
14884         // assume that we have an element reference instead of an id if the
14885         // parameter is not a string
14886         if (typeof id !== "string") {
14887             id = Roo.id(id);
14888         }
14889
14890         // set the id
14891         this.id = id;
14892
14893         // add to an interaction group
14894         this.addToGroup((sGroup) ? sGroup : "default");
14895
14896         // We don't want to register this as the handle with the manager
14897         // so we just set the id rather than calling the setter.
14898         this.handleElId = id;
14899
14900         // the linked element is the element that gets dragged by default
14901         this.setDragElId(id);
14902
14903         // by default, clicked anchors will not start drag operations.
14904         this.invalidHandleTypes = { A: "A" };
14905         this.invalidHandleIds = {};
14906         this.invalidHandleClasses = [];
14907
14908         this.applyConfig();
14909
14910         this.handleOnAvailable();
14911     },
14912
14913     /**
14914      * Applies the configuration parameters that were passed into the constructor.
14915      * This is supposed to happen at each level through the inheritance chain.  So
14916      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14917      * DragDrop in order to get all of the parameters that are available in
14918      * each object.
14919      * @method applyConfig
14920      */
14921     applyConfig: function() {
14922
14923         // configurable properties:
14924         //    padding, isTarget, maintainOffset, primaryButtonOnly
14925         this.padding           = this.config.padding || [0, 0, 0, 0];
14926         this.isTarget          = (this.config.isTarget !== false);
14927         this.maintainOffset    = (this.config.maintainOffset);
14928         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14929
14930     },
14931
14932     /**
14933      * Executed when the linked element is available
14934      * @method handleOnAvailable
14935      * @private
14936      */
14937     handleOnAvailable: function() {
14938         this.available = true;
14939         this.resetConstraints();
14940         this.onAvailable();
14941     },
14942
14943      /**
14944      * Configures the padding for the target zone in px.  Effectively expands
14945      * (or reduces) the virtual object size for targeting calculations.
14946      * Supports css-style shorthand; if only one parameter is passed, all sides
14947      * will have that padding, and if only two are passed, the top and bottom
14948      * will have the first param, the left and right the second.
14949      * @method setPadding
14950      * @param {int} iTop    Top pad
14951      * @param {int} iRight  Right pad
14952      * @param {int} iBot    Bot pad
14953      * @param {int} iLeft   Left pad
14954      */
14955     setPadding: function(iTop, iRight, iBot, iLeft) {
14956         // this.padding = [iLeft, iRight, iTop, iBot];
14957         if (!iRight && 0 !== iRight) {
14958             this.padding = [iTop, iTop, iTop, iTop];
14959         } else if (!iBot && 0 !== iBot) {
14960             this.padding = [iTop, iRight, iTop, iRight];
14961         } else {
14962             this.padding = [iTop, iRight, iBot, iLeft];
14963         }
14964     },
14965
14966     /**
14967      * Stores the initial placement of the linked element.
14968      * @method setInitialPosition
14969      * @param {int} diffX   the X offset, default 0
14970      * @param {int} diffY   the Y offset, default 0
14971      */
14972     setInitPosition: function(diffX, diffY) {
14973         var el = this.getEl();
14974
14975         if (!this.DDM.verifyEl(el)) {
14976             return;
14977         }
14978
14979         var dx = diffX || 0;
14980         var dy = diffY || 0;
14981
14982         var p = Dom.getXY( el );
14983
14984         this.initPageX = p[0] - dx;
14985         this.initPageY = p[1] - dy;
14986
14987         this.lastPageX = p[0];
14988         this.lastPageY = p[1];
14989
14990
14991         this.setStartPosition(p);
14992     },
14993
14994     /**
14995      * Sets the start position of the element.  This is set when the obj
14996      * is initialized, the reset when a drag is started.
14997      * @method setStartPosition
14998      * @param pos current position (from previous lookup)
14999      * @private
15000      */
15001     setStartPosition: function(pos) {
15002         var p = pos || Dom.getXY( this.getEl() );
15003         this.deltaSetXY = null;
15004
15005         this.startPageX = p[0];
15006         this.startPageY = p[1];
15007     },
15008
15009     /**
15010      * Add this instance to a group of related drag/drop objects.  All
15011      * instances belong to at least one group, and can belong to as many
15012      * groups as needed.
15013      * @method addToGroup
15014      * @param sGroup {string} the name of the group
15015      */
15016     addToGroup: function(sGroup) {
15017         this.groups[sGroup] = true;
15018         this.DDM.regDragDrop(this, sGroup);
15019     },
15020
15021     /**
15022      * Remove's this instance from the supplied interaction group
15023      * @method removeFromGroup
15024      * @param {string}  sGroup  The group to drop
15025      */
15026     removeFromGroup: function(sGroup) {
15027         if (this.groups[sGroup]) {
15028             delete this.groups[sGroup];
15029         }
15030
15031         this.DDM.removeDDFromGroup(this, sGroup);
15032     },
15033
15034     /**
15035      * Allows you to specify that an element other than the linked element
15036      * will be moved with the cursor during a drag
15037      * @method setDragElId
15038      * @param id {string} the id of the element that will be used to initiate the drag
15039      */
15040     setDragElId: function(id) {
15041         this.dragElId = id;
15042     },
15043
15044     /**
15045      * Allows you to specify a child of the linked element that should be
15046      * used to initiate the drag operation.  An example of this would be if
15047      * you have a content div with text and links.  Clicking anywhere in the
15048      * content area would normally start the drag operation.  Use this method
15049      * to specify that an element inside of the content div is the element
15050      * that starts the drag operation.
15051      * @method setHandleElId
15052      * @param id {string} the id of the element that will be used to
15053      * initiate the drag.
15054      */
15055     setHandleElId: function(id) {
15056         if (typeof id !== "string") {
15057             id = Roo.id(id);
15058         }
15059         this.handleElId = id;
15060         this.DDM.regHandle(this.id, id);
15061     },
15062
15063     /**
15064      * Allows you to set an element outside of the linked element as a drag
15065      * handle
15066      * @method setOuterHandleElId
15067      * @param id the id of the element that will be used to initiate the drag
15068      */
15069     setOuterHandleElId: function(id) {
15070         if (typeof id !== "string") {
15071             id = Roo.id(id);
15072         }
15073         Event.on(id, "mousedown",
15074                 this.handleMouseDown, this);
15075         this.setHandleElId(id);
15076
15077         this.hasOuterHandles = true;
15078     },
15079
15080     /**
15081      * Remove all drag and drop hooks for this element
15082      * @method unreg
15083      */
15084     unreg: function() {
15085         Event.un(this.id, "mousedown",
15086                 this.handleMouseDown);
15087         this._domRef = null;
15088         this.DDM._remove(this);
15089     },
15090
15091     destroy : function(){
15092         this.unreg();
15093     },
15094
15095     /**
15096      * Returns true if this instance is locked, or the drag drop mgr is locked
15097      * (meaning that all drag/drop is disabled on the page.)
15098      * @method isLocked
15099      * @return {boolean} true if this obj or all drag/drop is locked, else
15100      * false
15101      */
15102     isLocked: function() {
15103         return (this.DDM.isLocked() || this.locked);
15104     },
15105
15106     /**
15107      * Fired when this object is clicked
15108      * @method handleMouseDown
15109      * @param {Event} e
15110      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15111      * @private
15112      */
15113     handleMouseDown: function(e, oDD){
15114         if (this.primaryButtonOnly && e.button != 0) {
15115             return;
15116         }
15117
15118         if (this.isLocked()) {
15119             return;
15120         }
15121
15122         this.DDM.refreshCache(this.groups);
15123
15124         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15125         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15126         } else {
15127             if (this.clickValidator(e)) {
15128
15129                 // set the initial element position
15130                 this.setStartPosition();
15131
15132
15133                 this.b4MouseDown(e);
15134                 this.onMouseDown(e);
15135
15136                 this.DDM.handleMouseDown(e, this);
15137
15138                 this.DDM.stopEvent(e);
15139             } else {
15140
15141
15142             }
15143         }
15144     },
15145
15146     clickValidator: function(e) {
15147         var target = e.getTarget();
15148         return ( this.isValidHandleChild(target) &&
15149                     (this.id == this.handleElId ||
15150                         this.DDM.handleWasClicked(target, this.id)) );
15151     },
15152
15153     /**
15154      * Allows you to specify a tag name that should not start a drag operation
15155      * when clicked.  This is designed to facilitate embedding links within a
15156      * drag handle that do something other than start the drag.
15157      * @method addInvalidHandleType
15158      * @param {string} tagName the type of element to exclude
15159      */
15160     addInvalidHandleType: function(tagName) {
15161         var type = tagName.toUpperCase();
15162         this.invalidHandleTypes[type] = type;
15163     },
15164
15165     /**
15166      * Lets you to specify an element id for a child of a drag handle
15167      * that should not initiate a drag
15168      * @method addInvalidHandleId
15169      * @param {string} id the element id of the element you wish to ignore
15170      */
15171     addInvalidHandleId: function(id) {
15172         if (typeof id !== "string") {
15173             id = Roo.id(id);
15174         }
15175         this.invalidHandleIds[id] = id;
15176     },
15177
15178     /**
15179      * Lets you specify a css class of elements that will not initiate a drag
15180      * @method addInvalidHandleClass
15181      * @param {string} cssClass the class of the elements you wish to ignore
15182      */
15183     addInvalidHandleClass: function(cssClass) {
15184         this.invalidHandleClasses.push(cssClass);
15185     },
15186
15187     /**
15188      * Unsets an excluded tag name set by addInvalidHandleType
15189      * @method removeInvalidHandleType
15190      * @param {string} tagName the type of element to unexclude
15191      */
15192     removeInvalidHandleType: function(tagName) {
15193         var type = tagName.toUpperCase();
15194         // this.invalidHandleTypes[type] = null;
15195         delete this.invalidHandleTypes[type];
15196     },
15197
15198     /**
15199      * Unsets an invalid handle id
15200      * @method removeInvalidHandleId
15201      * @param {string} id the id of the element to re-enable
15202      */
15203     removeInvalidHandleId: function(id) {
15204         if (typeof id !== "string") {
15205             id = Roo.id(id);
15206         }
15207         delete this.invalidHandleIds[id];
15208     },
15209
15210     /**
15211      * Unsets an invalid css class
15212      * @method removeInvalidHandleClass
15213      * @param {string} cssClass the class of the element(s) you wish to
15214      * re-enable
15215      */
15216     removeInvalidHandleClass: function(cssClass) {
15217         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15218             if (this.invalidHandleClasses[i] == cssClass) {
15219                 delete this.invalidHandleClasses[i];
15220             }
15221         }
15222     },
15223
15224     /**
15225      * Checks the tag exclusion list to see if this click should be ignored
15226      * @method isValidHandleChild
15227      * @param {HTMLElement} node the HTMLElement to evaluate
15228      * @return {boolean} true if this is a valid tag type, false if not
15229      */
15230     isValidHandleChild: function(node) {
15231
15232         var valid = true;
15233         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15234         var nodeName;
15235         try {
15236             nodeName = node.nodeName.toUpperCase();
15237         } catch(e) {
15238             nodeName = node.nodeName;
15239         }
15240         valid = valid && !this.invalidHandleTypes[nodeName];
15241         valid = valid && !this.invalidHandleIds[node.id];
15242
15243         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15244             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15245         }
15246
15247
15248         return valid;
15249
15250     },
15251
15252     /**
15253      * Create the array of horizontal tick marks if an interval was specified
15254      * in setXConstraint().
15255      * @method setXTicks
15256      * @private
15257      */
15258     setXTicks: function(iStartX, iTickSize) {
15259         this.xTicks = [];
15260         this.xTickSize = iTickSize;
15261
15262         var tickMap = {};
15263
15264         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15265             if (!tickMap[i]) {
15266                 this.xTicks[this.xTicks.length] = i;
15267                 tickMap[i] = true;
15268             }
15269         }
15270
15271         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15272             if (!tickMap[i]) {
15273                 this.xTicks[this.xTicks.length] = i;
15274                 tickMap[i] = true;
15275             }
15276         }
15277
15278         this.xTicks.sort(this.DDM.numericSort) ;
15279     },
15280
15281     /**
15282      * Create the array of vertical tick marks if an interval was specified in
15283      * setYConstraint().
15284      * @method setYTicks
15285      * @private
15286      */
15287     setYTicks: function(iStartY, iTickSize) {
15288         this.yTicks = [];
15289         this.yTickSize = iTickSize;
15290
15291         var tickMap = {};
15292
15293         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15294             if (!tickMap[i]) {
15295                 this.yTicks[this.yTicks.length] = i;
15296                 tickMap[i] = true;
15297             }
15298         }
15299
15300         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15301             if (!tickMap[i]) {
15302                 this.yTicks[this.yTicks.length] = i;
15303                 tickMap[i] = true;
15304             }
15305         }
15306
15307         this.yTicks.sort(this.DDM.numericSort) ;
15308     },
15309
15310     /**
15311      * By default, the element can be dragged any place on the screen.  Use
15312      * this method to limit the horizontal travel of the element.  Pass in
15313      * 0,0 for the parameters if you want to lock the drag to the y axis.
15314      * @method setXConstraint
15315      * @param {int} iLeft the number of pixels the element can move to the left
15316      * @param {int} iRight the number of pixels the element can move to the
15317      * right
15318      * @param {int} iTickSize optional parameter for specifying that the
15319      * element
15320      * should move iTickSize pixels at a time.
15321      */
15322     setXConstraint: function(iLeft, iRight, iTickSize) {
15323         this.leftConstraint = iLeft;
15324         this.rightConstraint = iRight;
15325
15326         this.minX = this.initPageX - iLeft;
15327         this.maxX = this.initPageX + iRight;
15328         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15329
15330         this.constrainX = true;
15331     },
15332
15333     /**
15334      * Clears any constraints applied to this instance.  Also clears ticks
15335      * since they can't exist independent of a constraint at this time.
15336      * @method clearConstraints
15337      */
15338     clearConstraints: function() {
15339         this.constrainX = false;
15340         this.constrainY = false;
15341         this.clearTicks();
15342     },
15343
15344     /**
15345      * Clears any tick interval defined for this instance
15346      * @method clearTicks
15347      */
15348     clearTicks: function() {
15349         this.xTicks = null;
15350         this.yTicks = null;
15351         this.xTickSize = 0;
15352         this.yTickSize = 0;
15353     },
15354
15355     /**
15356      * By default, the element can be dragged any place on the screen.  Set
15357      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15358      * parameters if you want to lock the drag to the x axis.
15359      * @method setYConstraint
15360      * @param {int} iUp the number of pixels the element can move up
15361      * @param {int} iDown the number of pixels the element can move down
15362      * @param {int} iTickSize optional parameter for specifying that the
15363      * element should move iTickSize pixels at a time.
15364      */
15365     setYConstraint: function(iUp, iDown, iTickSize) {
15366         this.topConstraint = iUp;
15367         this.bottomConstraint = iDown;
15368
15369         this.minY = this.initPageY - iUp;
15370         this.maxY = this.initPageY + iDown;
15371         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15372
15373         this.constrainY = true;
15374
15375     },
15376
15377     /**
15378      * resetConstraints must be called if you manually reposition a dd element.
15379      * @method resetConstraints
15380      * @param {boolean} maintainOffset
15381      */
15382     resetConstraints: function() {
15383
15384
15385         // Maintain offsets if necessary
15386         if (this.initPageX || this.initPageX === 0) {
15387             // figure out how much this thing has moved
15388             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15389             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15390
15391             this.setInitPosition(dx, dy);
15392
15393         // This is the first time we have detected the element's position
15394         } else {
15395             this.setInitPosition();
15396         }
15397
15398         if (this.constrainX) {
15399             this.setXConstraint( this.leftConstraint,
15400                                  this.rightConstraint,
15401                                  this.xTickSize        );
15402         }
15403
15404         if (this.constrainY) {
15405             this.setYConstraint( this.topConstraint,
15406                                  this.bottomConstraint,
15407                                  this.yTickSize         );
15408         }
15409     },
15410
15411     /**
15412      * Normally the drag element is moved pixel by pixel, but we can specify
15413      * that it move a number of pixels at a time.  This method resolves the
15414      * location when we have it set up like this.
15415      * @method getTick
15416      * @param {int} val where we want to place the object
15417      * @param {int[]} tickArray sorted array of valid points
15418      * @return {int} the closest tick
15419      * @private
15420      */
15421     getTick: function(val, tickArray) {
15422
15423         if (!tickArray) {
15424             // If tick interval is not defined, it is effectively 1 pixel,
15425             // so we return the value passed to us.
15426             return val;
15427         } else if (tickArray[0] >= val) {
15428             // The value is lower than the first tick, so we return the first
15429             // tick.
15430             return tickArray[0];
15431         } else {
15432             for (var i=0, len=tickArray.length; i<len; ++i) {
15433                 var next = i + 1;
15434                 if (tickArray[next] && tickArray[next] >= val) {
15435                     var diff1 = val - tickArray[i];
15436                     var diff2 = tickArray[next] - val;
15437                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15438                 }
15439             }
15440
15441             // The value is larger than the last tick, so we return the last
15442             // tick.
15443             return tickArray[tickArray.length - 1];
15444         }
15445     },
15446
15447     /**
15448      * toString method
15449      * @method toString
15450      * @return {string} string representation of the dd obj
15451      */
15452     toString: function() {
15453         return ("DragDrop " + this.id);
15454     }
15455
15456 };
15457
15458 })();
15459 /*
15460  * Based on:
15461  * Ext JS Library 1.1.1
15462  * Copyright(c) 2006-2007, Ext JS, LLC.
15463  *
15464  * Originally Released Under LGPL - original licence link has changed is not relivant.
15465  *
15466  * Fork - LGPL
15467  * <script type="text/javascript">
15468  */
15469
15470
15471 /**
15472  * The drag and drop utility provides a framework for building drag and drop
15473  * applications.  In addition to enabling drag and drop for specific elements,
15474  * the drag and drop elements are tracked by the manager class, and the
15475  * interactions between the various elements are tracked during the drag and
15476  * the implementing code is notified about these important moments.
15477  */
15478
15479 // Only load the library once.  Rewriting the manager class would orphan
15480 // existing drag and drop instances.
15481 if (!Roo.dd.DragDropMgr) {
15482
15483 /**
15484  * @class Roo.dd.DragDropMgr
15485  * DragDropMgr is a singleton that tracks the element interaction for
15486  * all DragDrop items in the window.  Generally, you will not call
15487  * this class directly, but it does have helper methods that could
15488  * be useful in your DragDrop implementations.
15489  * @singleton
15490  */
15491 Roo.dd.DragDropMgr = function() {
15492
15493     var Event = Roo.EventManager;
15494
15495     return {
15496
15497         /**
15498          * Two dimensional Array of registered DragDrop objects.  The first
15499          * dimension is the DragDrop item group, the second the DragDrop
15500          * object.
15501          * @property ids
15502          * @type {string: string}
15503          * @private
15504          * @static
15505          */
15506         ids: {},
15507
15508         /**
15509          * Array of element ids defined as drag handles.  Used to determine
15510          * if the element that generated the mousedown event is actually the
15511          * handle and not the html element itself.
15512          * @property handleIds
15513          * @type {string: string}
15514          * @private
15515          * @static
15516          */
15517         handleIds: {},
15518
15519         /**
15520          * the DragDrop object that is currently being dragged
15521          * @property dragCurrent
15522          * @type DragDrop
15523          * @private
15524          * @static
15525          **/
15526         dragCurrent: null,
15527
15528         /**
15529          * the DragDrop object(s) that are being hovered over
15530          * @property dragOvers
15531          * @type Array
15532          * @private
15533          * @static
15534          */
15535         dragOvers: {},
15536
15537         /**
15538          * the X distance between the cursor and the object being dragged
15539          * @property deltaX
15540          * @type int
15541          * @private
15542          * @static
15543          */
15544         deltaX: 0,
15545
15546         /**
15547          * the Y distance between the cursor and the object being dragged
15548          * @property deltaY
15549          * @type int
15550          * @private
15551          * @static
15552          */
15553         deltaY: 0,
15554
15555         /**
15556          * Flag to determine if we should prevent the default behavior of the
15557          * events we define. By default this is true, but this can be set to
15558          * false if you need the default behavior (not recommended)
15559          * @property preventDefault
15560          * @type boolean
15561          * @static
15562          */
15563         preventDefault: true,
15564
15565         /**
15566          * Flag to determine if we should stop the propagation of the events
15567          * we generate. This is true by default but you may want to set it to
15568          * false if the html element contains other features that require the
15569          * mouse click.
15570          * @property stopPropagation
15571          * @type boolean
15572          * @static
15573          */
15574         stopPropagation: true,
15575
15576         /**
15577          * Internal flag that is set to true when drag and drop has been
15578          * intialized
15579          * @property initialized
15580          * @private
15581          * @static
15582          */
15583         initalized: false,
15584
15585         /**
15586          * All drag and drop can be disabled.
15587          * @property locked
15588          * @private
15589          * @static
15590          */
15591         locked: false,
15592
15593         /**
15594          * Called the first time an element is registered.
15595          * @method init
15596          * @private
15597          * @static
15598          */
15599         init: function() {
15600             this.initialized = true;
15601         },
15602
15603         /**
15604          * In point mode, drag and drop interaction is defined by the
15605          * location of the cursor during the drag/drop
15606          * @property POINT
15607          * @type int
15608          * @static
15609          */
15610         POINT: 0,
15611
15612         /**
15613          * In intersect mode, drag and drop interactio nis defined by the
15614          * overlap of two or more drag and drop objects.
15615          * @property INTERSECT
15616          * @type int
15617          * @static
15618          */
15619         INTERSECT: 1,
15620
15621         /**
15622          * The current drag and drop mode.  Default: POINT
15623          * @property mode
15624          * @type int
15625          * @static
15626          */
15627         mode: 0,
15628
15629         /**
15630          * Runs method on all drag and drop objects
15631          * @method _execOnAll
15632          * @private
15633          * @static
15634          */
15635         _execOnAll: function(sMethod, args) {
15636             for (var i in this.ids) {
15637                 for (var j in this.ids[i]) {
15638                     var oDD = this.ids[i][j];
15639                     if (! this.isTypeOfDD(oDD)) {
15640                         continue;
15641                     }
15642                     oDD[sMethod].apply(oDD, args);
15643                 }
15644             }
15645         },
15646
15647         /**
15648          * Drag and drop initialization.  Sets up the global event handlers
15649          * @method _onLoad
15650          * @private
15651          * @static
15652          */
15653         _onLoad: function() {
15654
15655             this.init();
15656
15657
15658             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15659             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15660             Event.on(window,   "unload",    this._onUnload, this, true);
15661             Event.on(window,   "resize",    this._onResize, this, true);
15662             // Event.on(window,   "mouseout",    this._test);
15663
15664         },
15665
15666         /**
15667          * Reset constraints on all drag and drop objs
15668          * @method _onResize
15669          * @private
15670          * @static
15671          */
15672         _onResize: function(e) {
15673             this._execOnAll("resetConstraints", []);
15674         },
15675
15676         /**
15677          * Lock all drag and drop functionality
15678          * @method lock
15679          * @static
15680          */
15681         lock: function() { this.locked = true; },
15682
15683         /**
15684          * Unlock all drag and drop functionality
15685          * @method unlock
15686          * @static
15687          */
15688         unlock: function() { this.locked = false; },
15689
15690         /**
15691          * Is drag and drop locked?
15692          * @method isLocked
15693          * @return {boolean} True if drag and drop is locked, false otherwise.
15694          * @static
15695          */
15696         isLocked: function() { return this.locked; },
15697
15698         /**
15699          * Location cache that is set for all drag drop objects when a drag is
15700          * initiated, cleared when the drag is finished.
15701          * @property locationCache
15702          * @private
15703          * @static
15704          */
15705         locationCache: {},
15706
15707         /**
15708          * Set useCache to false if you want to force object the lookup of each
15709          * drag and drop linked element constantly during a drag.
15710          * @property useCache
15711          * @type boolean
15712          * @static
15713          */
15714         useCache: true,
15715
15716         /**
15717          * The number of pixels that the mouse needs to move after the
15718          * mousedown before the drag is initiated.  Default=3;
15719          * @property clickPixelThresh
15720          * @type int
15721          * @static
15722          */
15723         clickPixelThresh: 3,
15724
15725         /**
15726          * The number of milliseconds after the mousedown event to initiate the
15727          * drag if we don't get a mouseup event. Default=1000
15728          * @property clickTimeThresh
15729          * @type int
15730          * @static
15731          */
15732         clickTimeThresh: 350,
15733
15734         /**
15735          * Flag that indicates that either the drag pixel threshold or the
15736          * mousdown time threshold has been met
15737          * @property dragThreshMet
15738          * @type boolean
15739          * @private
15740          * @static
15741          */
15742         dragThreshMet: false,
15743
15744         /**
15745          * Timeout used for the click time threshold
15746          * @property clickTimeout
15747          * @type Object
15748          * @private
15749          * @static
15750          */
15751         clickTimeout: null,
15752
15753         /**
15754          * The X position of the mousedown event stored for later use when a
15755          * drag threshold is met.
15756          * @property startX
15757          * @type int
15758          * @private
15759          * @static
15760          */
15761         startX: 0,
15762
15763         /**
15764          * The Y position of the mousedown event stored for later use when a
15765          * drag threshold is met.
15766          * @property startY
15767          * @type int
15768          * @private
15769          * @static
15770          */
15771         startY: 0,
15772
15773         /**
15774          * Each DragDrop instance must be registered with the DragDropMgr.
15775          * This is executed in DragDrop.init()
15776          * @method regDragDrop
15777          * @param {DragDrop} oDD the DragDrop object to register
15778          * @param {String} sGroup the name of the group this element belongs to
15779          * @static
15780          */
15781         regDragDrop: function(oDD, sGroup) {
15782             if (!this.initialized) { this.init(); }
15783
15784             if (!this.ids[sGroup]) {
15785                 this.ids[sGroup] = {};
15786             }
15787             this.ids[sGroup][oDD.id] = oDD;
15788         },
15789
15790         /**
15791          * Removes the supplied dd instance from the supplied group. Executed
15792          * by DragDrop.removeFromGroup, so don't call this function directly.
15793          * @method removeDDFromGroup
15794          * @private
15795          * @static
15796          */
15797         removeDDFromGroup: function(oDD, sGroup) {
15798             if (!this.ids[sGroup]) {
15799                 this.ids[sGroup] = {};
15800             }
15801
15802             var obj = this.ids[sGroup];
15803             if (obj && obj[oDD.id]) {
15804                 delete obj[oDD.id];
15805             }
15806         },
15807
15808         /**
15809          * Unregisters a drag and drop item.  This is executed in
15810          * DragDrop.unreg, use that method instead of calling this directly.
15811          * @method _remove
15812          * @private
15813          * @static
15814          */
15815         _remove: function(oDD) {
15816             for (var g in oDD.groups) {
15817                 if (g && this.ids[g][oDD.id]) {
15818                     delete this.ids[g][oDD.id];
15819                 }
15820             }
15821             delete this.handleIds[oDD.id];
15822         },
15823
15824         /**
15825          * Each DragDrop handle element must be registered.  This is done
15826          * automatically when executing DragDrop.setHandleElId()
15827          * @method regHandle
15828          * @param {String} sDDId the DragDrop id this element is a handle for
15829          * @param {String} sHandleId the id of the element that is the drag
15830          * handle
15831          * @static
15832          */
15833         regHandle: function(sDDId, sHandleId) {
15834             if (!this.handleIds[sDDId]) {
15835                 this.handleIds[sDDId] = {};
15836             }
15837             this.handleIds[sDDId][sHandleId] = sHandleId;
15838         },
15839
15840         /**
15841          * Utility function to determine if a given element has been
15842          * registered as a drag drop item.
15843          * @method isDragDrop
15844          * @param {String} id the element id to check
15845          * @return {boolean} true if this element is a DragDrop item,
15846          * false otherwise
15847          * @static
15848          */
15849         isDragDrop: function(id) {
15850             return ( this.getDDById(id) ) ? true : false;
15851         },
15852
15853         /**
15854          * Returns the drag and drop instances that are in all groups the
15855          * passed in instance belongs to.
15856          * @method getRelated
15857          * @param {DragDrop} p_oDD the obj to get related data for
15858          * @param {boolean} bTargetsOnly if true, only return targetable objs
15859          * @return {DragDrop[]} the related instances
15860          * @static
15861          */
15862         getRelated: function(p_oDD, bTargetsOnly) {
15863             var oDDs = [];
15864             for (var i in p_oDD.groups) {
15865                 for (j in this.ids[i]) {
15866                     var dd = this.ids[i][j];
15867                     if (! this.isTypeOfDD(dd)) {
15868                         continue;
15869                     }
15870                     if (!bTargetsOnly || dd.isTarget) {
15871                         oDDs[oDDs.length] = dd;
15872                     }
15873                 }
15874             }
15875
15876             return oDDs;
15877         },
15878
15879         /**
15880          * Returns true if the specified dd target is a legal target for
15881          * the specifice drag obj
15882          * @method isLegalTarget
15883          * @param {DragDrop} the drag obj
15884          * @param {DragDrop} the target
15885          * @return {boolean} true if the target is a legal target for the
15886          * dd obj
15887          * @static
15888          */
15889         isLegalTarget: function (oDD, oTargetDD) {
15890             var targets = this.getRelated(oDD, true);
15891             for (var i=0, len=targets.length;i<len;++i) {
15892                 if (targets[i].id == oTargetDD.id) {
15893                     return true;
15894                 }
15895             }
15896
15897             return false;
15898         },
15899
15900         /**
15901          * My goal is to be able to transparently determine if an object is
15902          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15903          * returns "object", oDD.constructor.toString() always returns
15904          * "DragDrop" and not the name of the subclass.  So for now it just
15905          * evaluates a well-known variable in DragDrop.
15906          * @method isTypeOfDD
15907          * @param {Object} the object to evaluate
15908          * @return {boolean} true if typeof oDD = DragDrop
15909          * @static
15910          */
15911         isTypeOfDD: function (oDD) {
15912             return (oDD && oDD.__ygDragDrop);
15913         },
15914
15915         /**
15916          * Utility function to determine if a given element has been
15917          * registered as a drag drop handle for the given Drag Drop object.
15918          * @method isHandle
15919          * @param {String} id the element id to check
15920          * @return {boolean} true if this element is a DragDrop handle, false
15921          * otherwise
15922          * @static
15923          */
15924         isHandle: function(sDDId, sHandleId) {
15925             return ( this.handleIds[sDDId] &&
15926                             this.handleIds[sDDId][sHandleId] );
15927         },
15928
15929         /**
15930          * Returns the DragDrop instance for a given id
15931          * @method getDDById
15932          * @param {String} id the id of the DragDrop object
15933          * @return {DragDrop} the drag drop object, null if it is not found
15934          * @static
15935          */
15936         getDDById: function(id) {
15937             for (var i in this.ids) {
15938                 if (this.ids[i][id]) {
15939                     return this.ids[i][id];
15940                 }
15941             }
15942             return null;
15943         },
15944
15945         /**
15946          * Fired after a registered DragDrop object gets the mousedown event.
15947          * Sets up the events required to track the object being dragged
15948          * @method handleMouseDown
15949          * @param {Event} e the event
15950          * @param oDD the DragDrop object being dragged
15951          * @private
15952          * @static
15953          */
15954         handleMouseDown: function(e, oDD) {
15955             if(Roo.QuickTips){
15956                 Roo.QuickTips.disable();
15957             }
15958             this.currentTarget = e.getTarget();
15959
15960             this.dragCurrent = oDD;
15961
15962             var el = oDD.getEl();
15963
15964             // track start position
15965             this.startX = e.getPageX();
15966             this.startY = e.getPageY();
15967
15968             this.deltaX = this.startX - el.offsetLeft;
15969             this.deltaY = this.startY - el.offsetTop;
15970
15971             this.dragThreshMet = false;
15972
15973             this.clickTimeout = setTimeout(
15974                     function() {
15975                         var DDM = Roo.dd.DDM;
15976                         DDM.startDrag(DDM.startX, DDM.startY);
15977                     },
15978                     this.clickTimeThresh );
15979         },
15980
15981         /**
15982          * Fired when either the drag pixel threshol or the mousedown hold
15983          * time threshold has been met.
15984          * @method startDrag
15985          * @param x {int} the X position of the original mousedown
15986          * @param y {int} the Y position of the original mousedown
15987          * @static
15988          */
15989         startDrag: function(x, y) {
15990             clearTimeout(this.clickTimeout);
15991             if (this.dragCurrent) {
15992                 this.dragCurrent.b4StartDrag(x, y);
15993                 this.dragCurrent.startDrag(x, y);
15994             }
15995             this.dragThreshMet = true;
15996         },
15997
15998         /**
15999          * Internal function to handle the mouseup event.  Will be invoked
16000          * from the context of the document.
16001          * @method handleMouseUp
16002          * @param {Event} e the event
16003          * @private
16004          * @static
16005          */
16006         handleMouseUp: function(e) {
16007
16008             if(Roo.QuickTips){
16009                 Roo.QuickTips.enable();
16010             }
16011             if (! this.dragCurrent) {
16012                 return;
16013             }
16014
16015             clearTimeout(this.clickTimeout);
16016
16017             if (this.dragThreshMet) {
16018                 this.fireEvents(e, true);
16019             } else {
16020             }
16021
16022             this.stopDrag(e);
16023
16024             this.stopEvent(e);
16025         },
16026
16027         /**
16028          * Utility to stop event propagation and event default, if these
16029          * features are turned on.
16030          * @method stopEvent
16031          * @param {Event} e the event as returned by this.getEvent()
16032          * @static
16033          */
16034         stopEvent: function(e){
16035             if(this.stopPropagation) {
16036                 e.stopPropagation();
16037             }
16038
16039             if (this.preventDefault) {
16040                 e.preventDefault();
16041             }
16042         },
16043
16044         /**
16045          * Internal function to clean up event handlers after the drag
16046          * operation is complete
16047          * @method stopDrag
16048          * @param {Event} e the event
16049          * @private
16050          * @static
16051          */
16052         stopDrag: function(e) {
16053             // Fire the drag end event for the item that was dragged
16054             if (this.dragCurrent) {
16055                 if (this.dragThreshMet) {
16056                     this.dragCurrent.b4EndDrag(e);
16057                     this.dragCurrent.endDrag(e);
16058                 }
16059
16060                 this.dragCurrent.onMouseUp(e);
16061             }
16062
16063             this.dragCurrent = null;
16064             this.dragOvers = {};
16065         },
16066
16067         /**
16068          * Internal function to handle the mousemove event.  Will be invoked
16069          * from the context of the html element.
16070          *
16071          * @TODO figure out what we can do about mouse events lost when the
16072          * user drags objects beyond the window boundary.  Currently we can
16073          * detect this in internet explorer by verifying that the mouse is
16074          * down during the mousemove event.  Firefox doesn't give us the
16075          * button state on the mousemove event.
16076          * @method handleMouseMove
16077          * @param {Event} e the event
16078          * @private
16079          * @static
16080          */
16081         handleMouseMove: function(e) {
16082             if (! this.dragCurrent) {
16083                 return true;
16084             }
16085
16086             // var button = e.which || e.button;
16087
16088             // check for IE mouseup outside of page boundary
16089             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16090                 this.stopEvent(e);
16091                 return this.handleMouseUp(e);
16092             }
16093
16094             if (!this.dragThreshMet) {
16095                 var diffX = Math.abs(this.startX - e.getPageX());
16096                 var diffY = Math.abs(this.startY - e.getPageY());
16097                 if (diffX > this.clickPixelThresh ||
16098                             diffY > this.clickPixelThresh) {
16099                     this.startDrag(this.startX, this.startY);
16100                 }
16101             }
16102
16103             if (this.dragThreshMet) {
16104                 this.dragCurrent.b4Drag(e);
16105                 this.dragCurrent.onDrag(e);
16106                 if(!this.dragCurrent.moveOnly){
16107                     this.fireEvents(e, false);
16108                 }
16109             }
16110
16111             this.stopEvent(e);
16112
16113             return true;
16114         },
16115
16116         /**
16117          * Iterates over all of the DragDrop elements to find ones we are
16118          * hovering over or dropping on
16119          * @method fireEvents
16120          * @param {Event} e the event
16121          * @param {boolean} isDrop is this a drop op or a mouseover op?
16122          * @private
16123          * @static
16124          */
16125         fireEvents: function(e, isDrop) {
16126             var dc = this.dragCurrent;
16127
16128             // If the user did the mouse up outside of the window, we could
16129             // get here even though we have ended the drag.
16130             if (!dc || dc.isLocked()) {
16131                 return;
16132             }
16133
16134             var pt = e.getPoint();
16135
16136             // cache the previous dragOver array
16137             var oldOvers = [];
16138
16139             var outEvts   = [];
16140             var overEvts  = [];
16141             var dropEvts  = [];
16142             var enterEvts = [];
16143
16144             // Check to see if the object(s) we were hovering over is no longer
16145             // being hovered over so we can fire the onDragOut event
16146             for (var i in this.dragOvers) {
16147
16148                 var ddo = this.dragOvers[i];
16149
16150                 if (! this.isTypeOfDD(ddo)) {
16151                     continue;
16152                 }
16153
16154                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16155                     outEvts.push( ddo );
16156                 }
16157
16158                 oldOvers[i] = true;
16159                 delete this.dragOvers[i];
16160             }
16161
16162             for (var sGroup in dc.groups) {
16163
16164                 if ("string" != typeof sGroup) {
16165                     continue;
16166                 }
16167
16168                 for (i in this.ids[sGroup]) {
16169                     var oDD = this.ids[sGroup][i];
16170                     if (! this.isTypeOfDD(oDD)) {
16171                         continue;
16172                     }
16173
16174                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16175                         if (this.isOverTarget(pt, oDD, this.mode)) {
16176                             // look for drop interactions
16177                             if (isDrop) {
16178                                 dropEvts.push( oDD );
16179                             // look for drag enter and drag over interactions
16180                             } else {
16181
16182                                 // initial drag over: dragEnter fires
16183                                 if (!oldOvers[oDD.id]) {
16184                                     enterEvts.push( oDD );
16185                                 // subsequent drag overs: dragOver fires
16186                                 } else {
16187                                     overEvts.push( oDD );
16188                                 }
16189
16190                                 this.dragOvers[oDD.id] = oDD;
16191                             }
16192                         }
16193                     }
16194                 }
16195             }
16196
16197             if (this.mode) {
16198                 if (outEvts.length) {
16199                     dc.b4DragOut(e, outEvts);
16200                     dc.onDragOut(e, outEvts);
16201                 }
16202
16203                 if (enterEvts.length) {
16204                     dc.onDragEnter(e, enterEvts);
16205                 }
16206
16207                 if (overEvts.length) {
16208                     dc.b4DragOver(e, overEvts);
16209                     dc.onDragOver(e, overEvts);
16210                 }
16211
16212                 if (dropEvts.length) {
16213                     dc.b4DragDrop(e, dropEvts);
16214                     dc.onDragDrop(e, dropEvts);
16215                 }
16216
16217             } else {
16218                 // fire dragout events
16219                 var len = 0;
16220                 for (i=0, len=outEvts.length; i<len; ++i) {
16221                     dc.b4DragOut(e, outEvts[i].id);
16222                     dc.onDragOut(e, outEvts[i].id);
16223                 }
16224
16225                 // fire enter events
16226                 for (i=0,len=enterEvts.length; i<len; ++i) {
16227                     // dc.b4DragEnter(e, oDD.id);
16228                     dc.onDragEnter(e, enterEvts[i].id);
16229                 }
16230
16231                 // fire over events
16232                 for (i=0,len=overEvts.length; i<len; ++i) {
16233                     dc.b4DragOver(e, overEvts[i].id);
16234                     dc.onDragOver(e, overEvts[i].id);
16235                 }
16236
16237                 // fire drop events
16238                 for (i=0, len=dropEvts.length; i<len; ++i) {
16239                     dc.b4DragDrop(e, dropEvts[i].id);
16240                     dc.onDragDrop(e, dropEvts[i].id);
16241                 }
16242
16243             }
16244
16245             // notify about a drop that did not find a target
16246             if (isDrop && !dropEvts.length) {
16247                 dc.onInvalidDrop(e);
16248             }
16249
16250         },
16251
16252         /**
16253          * Helper function for getting the best match from the list of drag
16254          * and drop objects returned by the drag and drop events when we are
16255          * in INTERSECT mode.  It returns either the first object that the
16256          * cursor is over, or the object that has the greatest overlap with
16257          * the dragged element.
16258          * @method getBestMatch
16259          * @param  {DragDrop[]} dds The array of drag and drop objects
16260          * targeted
16261          * @return {DragDrop}       The best single match
16262          * @static
16263          */
16264         getBestMatch: function(dds) {
16265             var winner = null;
16266             // Return null if the input is not what we expect
16267             //if (!dds || !dds.length || dds.length == 0) {
16268                // winner = null;
16269             // If there is only one item, it wins
16270             //} else if (dds.length == 1) {
16271
16272             var len = dds.length;
16273
16274             if (len == 1) {
16275                 winner = dds[0];
16276             } else {
16277                 // Loop through the targeted items
16278                 for (var i=0; i<len; ++i) {
16279                     var dd = dds[i];
16280                     // If the cursor is over the object, it wins.  If the
16281                     // cursor is over multiple matches, the first one we come
16282                     // to wins.
16283                     if (dd.cursorIsOver) {
16284                         winner = dd;
16285                         break;
16286                     // Otherwise the object with the most overlap wins
16287                     } else {
16288                         if (!winner ||
16289                             winner.overlap.getArea() < dd.overlap.getArea()) {
16290                             winner = dd;
16291                         }
16292                     }
16293                 }
16294             }
16295
16296             return winner;
16297         },
16298
16299         /**
16300          * Refreshes the cache of the top-left and bottom-right points of the
16301          * drag and drop objects in the specified group(s).  This is in the
16302          * format that is stored in the drag and drop instance, so typical
16303          * usage is:
16304          * <code>
16305          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16306          * </code>
16307          * Alternatively:
16308          * <code>
16309          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16310          * </code>
16311          * @TODO this really should be an indexed array.  Alternatively this
16312          * method could accept both.
16313          * @method refreshCache
16314          * @param {Object} groups an associative array of groups to refresh
16315          * @static
16316          */
16317         refreshCache: function(groups) {
16318             for (var sGroup in groups) {
16319                 if ("string" != typeof sGroup) {
16320                     continue;
16321                 }
16322                 for (var i in this.ids[sGroup]) {
16323                     var oDD = this.ids[sGroup][i];
16324
16325                     if (this.isTypeOfDD(oDD)) {
16326                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16327                         var loc = this.getLocation(oDD);
16328                         if (loc) {
16329                             this.locationCache[oDD.id] = loc;
16330                         } else {
16331                             delete this.locationCache[oDD.id];
16332                             // this will unregister the drag and drop object if
16333                             // the element is not in a usable state
16334                             // oDD.unreg();
16335                         }
16336                     }
16337                 }
16338             }
16339         },
16340
16341         /**
16342          * This checks to make sure an element exists and is in the DOM.  The
16343          * main purpose is to handle cases where innerHTML is used to remove
16344          * drag and drop objects from the DOM.  IE provides an 'unspecified
16345          * error' when trying to access the offsetParent of such an element
16346          * @method verifyEl
16347          * @param {HTMLElement} el the element to check
16348          * @return {boolean} true if the element looks usable
16349          * @static
16350          */
16351         verifyEl: function(el) {
16352             if (el) {
16353                 var parent;
16354                 if(Roo.isIE){
16355                     try{
16356                         parent = el.offsetParent;
16357                     }catch(e){}
16358                 }else{
16359                     parent = el.offsetParent;
16360                 }
16361                 if (parent) {
16362                     return true;
16363                 }
16364             }
16365
16366             return false;
16367         },
16368
16369         /**
16370          * Returns a Region object containing the drag and drop element's position
16371          * and size, including the padding configured for it
16372          * @method getLocation
16373          * @param {DragDrop} oDD the drag and drop object to get the
16374          *                       location for
16375          * @return {Roo.lib.Region} a Region object representing the total area
16376          *                             the element occupies, including any padding
16377          *                             the instance is configured for.
16378          * @static
16379          */
16380         getLocation: function(oDD) {
16381             if (! this.isTypeOfDD(oDD)) {
16382                 return null;
16383             }
16384
16385             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16386
16387             try {
16388                 pos= Roo.lib.Dom.getXY(el);
16389             } catch (e) { }
16390
16391             if (!pos) {
16392                 return null;
16393             }
16394
16395             x1 = pos[0];
16396             x2 = x1 + el.offsetWidth;
16397             y1 = pos[1];
16398             y2 = y1 + el.offsetHeight;
16399
16400             t = y1 - oDD.padding[0];
16401             r = x2 + oDD.padding[1];
16402             b = y2 + oDD.padding[2];
16403             l = x1 - oDD.padding[3];
16404
16405             return new Roo.lib.Region( t, r, b, l );
16406         },
16407
16408         /**
16409          * Checks the cursor location to see if it over the target
16410          * @method isOverTarget
16411          * @param {Roo.lib.Point} pt The point to evaluate
16412          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16413          * @return {boolean} true if the mouse is over the target
16414          * @private
16415          * @static
16416          */
16417         isOverTarget: function(pt, oTarget, intersect) {
16418             // use cache if available
16419             var loc = this.locationCache[oTarget.id];
16420             if (!loc || !this.useCache) {
16421                 loc = this.getLocation(oTarget);
16422                 this.locationCache[oTarget.id] = loc;
16423
16424             }
16425
16426             if (!loc) {
16427                 return false;
16428             }
16429
16430             oTarget.cursorIsOver = loc.contains( pt );
16431
16432             // DragDrop is using this as a sanity check for the initial mousedown
16433             // in this case we are done.  In POINT mode, if the drag obj has no
16434             // contraints, we are also done. Otherwise we need to evaluate the
16435             // location of the target as related to the actual location of the
16436             // dragged element.
16437             var dc = this.dragCurrent;
16438             if (!dc || !dc.getTargetCoord ||
16439                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16440                 return oTarget.cursorIsOver;
16441             }
16442
16443             oTarget.overlap = null;
16444
16445             // Get the current location of the drag element, this is the
16446             // location of the mouse event less the delta that represents
16447             // where the original mousedown happened on the element.  We
16448             // need to consider constraints and ticks as well.
16449             var pos = dc.getTargetCoord(pt.x, pt.y);
16450
16451             var el = dc.getDragEl();
16452             var curRegion = new Roo.lib.Region( pos.y,
16453                                                    pos.x + el.offsetWidth,
16454                                                    pos.y + el.offsetHeight,
16455                                                    pos.x );
16456
16457             var overlap = curRegion.intersect(loc);
16458
16459             if (overlap) {
16460                 oTarget.overlap = overlap;
16461                 return (intersect) ? true : oTarget.cursorIsOver;
16462             } else {
16463                 return false;
16464             }
16465         },
16466
16467         /**
16468          * unload event handler
16469          * @method _onUnload
16470          * @private
16471          * @static
16472          */
16473         _onUnload: function(e, me) {
16474             Roo.dd.DragDropMgr.unregAll();
16475         },
16476
16477         /**
16478          * Cleans up the drag and drop events and objects.
16479          * @method unregAll
16480          * @private
16481          * @static
16482          */
16483         unregAll: function() {
16484
16485             if (this.dragCurrent) {
16486                 this.stopDrag();
16487                 this.dragCurrent = null;
16488             }
16489
16490             this._execOnAll("unreg", []);
16491
16492             for (i in this.elementCache) {
16493                 delete this.elementCache[i];
16494             }
16495
16496             this.elementCache = {};
16497             this.ids = {};
16498         },
16499
16500         /**
16501          * A cache of DOM elements
16502          * @property elementCache
16503          * @private
16504          * @static
16505          */
16506         elementCache: {},
16507
16508         /**
16509          * Get the wrapper for the DOM element specified
16510          * @method getElWrapper
16511          * @param {String} id the id of the element to get
16512          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16513          * @private
16514          * @deprecated This wrapper isn't that useful
16515          * @static
16516          */
16517         getElWrapper: function(id) {
16518             var oWrapper = this.elementCache[id];
16519             if (!oWrapper || !oWrapper.el) {
16520                 oWrapper = this.elementCache[id] =
16521                     new this.ElementWrapper(Roo.getDom(id));
16522             }
16523             return oWrapper;
16524         },
16525
16526         /**
16527          * Returns the actual DOM element
16528          * @method getElement
16529          * @param {String} id the id of the elment to get
16530          * @return {Object} The element
16531          * @deprecated use Roo.getDom instead
16532          * @static
16533          */
16534         getElement: function(id) {
16535             return Roo.getDom(id);
16536         },
16537
16538         /**
16539          * Returns the style property for the DOM element (i.e.,
16540          * document.getElById(id).style)
16541          * @method getCss
16542          * @param {String} id the id of the elment to get
16543          * @return {Object} The style property of the element
16544          * @deprecated use Roo.getDom instead
16545          * @static
16546          */
16547         getCss: function(id) {
16548             var el = Roo.getDom(id);
16549             return (el) ? el.style : null;
16550         },
16551
16552         /**
16553          * Inner class for cached elements
16554          * @class DragDropMgr.ElementWrapper
16555          * @for DragDropMgr
16556          * @private
16557          * @deprecated
16558          */
16559         ElementWrapper: function(el) {
16560                 /**
16561                  * The element
16562                  * @property el
16563                  */
16564                 this.el = el || null;
16565                 /**
16566                  * The element id
16567                  * @property id
16568                  */
16569                 this.id = this.el && el.id;
16570                 /**
16571                  * A reference to the style property
16572                  * @property css
16573                  */
16574                 this.css = this.el && el.style;
16575             },
16576
16577         /**
16578          * Returns the X position of an html element
16579          * @method getPosX
16580          * @param el the element for which to get the position
16581          * @return {int} the X coordinate
16582          * @for DragDropMgr
16583          * @deprecated use Roo.lib.Dom.getX instead
16584          * @static
16585          */
16586         getPosX: function(el) {
16587             return Roo.lib.Dom.getX(el);
16588         },
16589
16590         /**
16591          * Returns the Y position of an html element
16592          * @method getPosY
16593          * @param el the element for which to get the position
16594          * @return {int} the Y coordinate
16595          * @deprecated use Roo.lib.Dom.getY instead
16596          * @static
16597          */
16598         getPosY: function(el) {
16599             return Roo.lib.Dom.getY(el);
16600         },
16601
16602         /**
16603          * Swap two nodes.  In IE, we use the native method, for others we
16604          * emulate the IE behavior
16605          * @method swapNode
16606          * @param n1 the first node to swap
16607          * @param n2 the other node to swap
16608          * @static
16609          */
16610         swapNode: function(n1, n2) {
16611             if (n1.swapNode) {
16612                 n1.swapNode(n2);
16613             } else {
16614                 var p = n2.parentNode;
16615                 var s = n2.nextSibling;
16616
16617                 if (s == n1) {
16618                     p.insertBefore(n1, n2);
16619                 } else if (n2 == n1.nextSibling) {
16620                     p.insertBefore(n2, n1);
16621                 } else {
16622                     n1.parentNode.replaceChild(n2, n1);
16623                     p.insertBefore(n1, s);
16624                 }
16625             }
16626         },
16627
16628         /**
16629          * Returns the current scroll position
16630          * @method getScroll
16631          * @private
16632          * @static
16633          */
16634         getScroll: function () {
16635             var t, l, dde=document.documentElement, db=document.body;
16636             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16637                 t = dde.scrollTop;
16638                 l = dde.scrollLeft;
16639             } else if (db) {
16640                 t = db.scrollTop;
16641                 l = db.scrollLeft;
16642             } else {
16643
16644             }
16645             return { top: t, left: l };
16646         },
16647
16648         /**
16649          * Returns the specified element style property
16650          * @method getStyle
16651          * @param {HTMLElement} el          the element
16652          * @param {string}      styleProp   the style property
16653          * @return {string} The value of the style property
16654          * @deprecated use Roo.lib.Dom.getStyle
16655          * @static
16656          */
16657         getStyle: function(el, styleProp) {
16658             return Roo.fly(el).getStyle(styleProp);
16659         },
16660
16661         /**
16662          * Gets the scrollTop
16663          * @method getScrollTop
16664          * @return {int} the document's scrollTop
16665          * @static
16666          */
16667         getScrollTop: function () { return this.getScroll().top; },
16668
16669         /**
16670          * Gets the scrollLeft
16671          * @method getScrollLeft
16672          * @return {int} the document's scrollTop
16673          * @static
16674          */
16675         getScrollLeft: function () { return this.getScroll().left; },
16676
16677         /**
16678          * Sets the x/y position of an element to the location of the
16679          * target element.
16680          * @method moveToEl
16681          * @param {HTMLElement} moveEl      The element to move
16682          * @param {HTMLElement} targetEl    The position reference element
16683          * @static
16684          */
16685         moveToEl: function (moveEl, targetEl) {
16686             var aCoord = Roo.lib.Dom.getXY(targetEl);
16687             Roo.lib.Dom.setXY(moveEl, aCoord);
16688         },
16689
16690         /**
16691          * Numeric array sort function
16692          * @method numericSort
16693          * @static
16694          */
16695         numericSort: function(a, b) { return (a - b); },
16696
16697         /**
16698          * Internal counter
16699          * @property _timeoutCount
16700          * @private
16701          * @static
16702          */
16703         _timeoutCount: 0,
16704
16705         /**
16706          * Trying to make the load order less important.  Without this we get
16707          * an error if this file is loaded before the Event Utility.
16708          * @method _addListeners
16709          * @private
16710          * @static
16711          */
16712         _addListeners: function() {
16713             var DDM = Roo.dd.DDM;
16714             if ( Roo.lib.Event && document ) {
16715                 DDM._onLoad();
16716             } else {
16717                 if (DDM._timeoutCount > 2000) {
16718                 } else {
16719                     setTimeout(DDM._addListeners, 10);
16720                     if (document && document.body) {
16721                         DDM._timeoutCount += 1;
16722                     }
16723                 }
16724             }
16725         },
16726
16727         /**
16728          * Recursively searches the immediate parent and all child nodes for
16729          * the handle element in order to determine wheter or not it was
16730          * clicked.
16731          * @method handleWasClicked
16732          * @param node the html element to inspect
16733          * @static
16734          */
16735         handleWasClicked: function(node, id) {
16736             if (this.isHandle(id, node.id)) {
16737                 return true;
16738             } else {
16739                 // check to see if this is a text node child of the one we want
16740                 var p = node.parentNode;
16741
16742                 while (p) {
16743                     if (this.isHandle(id, p.id)) {
16744                         return true;
16745                     } else {
16746                         p = p.parentNode;
16747                     }
16748                 }
16749             }
16750
16751             return false;
16752         }
16753
16754     };
16755
16756 }();
16757
16758 // shorter alias, save a few bytes
16759 Roo.dd.DDM = Roo.dd.DragDropMgr;
16760 Roo.dd.DDM._addListeners();
16761
16762 }/*
16763  * Based on:
16764  * Ext JS Library 1.1.1
16765  * Copyright(c) 2006-2007, Ext JS, LLC.
16766  *
16767  * Originally Released Under LGPL - original licence link has changed is not relivant.
16768  *
16769  * Fork - LGPL
16770  * <script type="text/javascript">
16771  */
16772
16773 /**
16774  * @class Roo.dd.DD
16775  * A DragDrop implementation where the linked element follows the
16776  * mouse cursor during a drag.
16777  * @extends Roo.dd.DragDrop
16778  * @constructor
16779  * @param {String} id the id of the linked element
16780  * @param {String} sGroup the group of related DragDrop items
16781  * @param {object} config an object containing configurable attributes
16782  *                Valid properties for DD:
16783  *                    scroll
16784  */
16785 Roo.dd.DD = function(id, sGroup, config) {
16786     if (id) {
16787         this.init(id, sGroup, config);
16788     }
16789 };
16790
16791 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16792
16793     /**
16794      * When set to true, the utility automatically tries to scroll the browser
16795      * window wehn a drag and drop element is dragged near the viewport boundary.
16796      * Defaults to true.
16797      * @property scroll
16798      * @type boolean
16799      */
16800     scroll: true,
16801
16802     /**
16803      * Sets the pointer offset to the distance between the linked element's top
16804      * left corner and the location the element was clicked
16805      * @method autoOffset
16806      * @param {int} iPageX the X coordinate of the click
16807      * @param {int} iPageY the Y coordinate of the click
16808      */
16809     autoOffset: function(iPageX, iPageY) {
16810         var x = iPageX - this.startPageX;
16811         var y = iPageY - this.startPageY;
16812         this.setDelta(x, y);
16813     },
16814
16815     /**
16816      * Sets the pointer offset.  You can call this directly to force the
16817      * offset to be in a particular location (e.g., pass in 0,0 to set it
16818      * to the center of the object)
16819      * @method setDelta
16820      * @param {int} iDeltaX the distance from the left
16821      * @param {int} iDeltaY the distance from the top
16822      */
16823     setDelta: function(iDeltaX, iDeltaY) {
16824         this.deltaX = iDeltaX;
16825         this.deltaY = iDeltaY;
16826     },
16827
16828     /**
16829      * Sets the drag element to the location of the mousedown or click event,
16830      * maintaining the cursor location relative to the location on the element
16831      * that was clicked.  Override this if you want to place the element in a
16832      * location other than where the cursor is.
16833      * @method setDragElPos
16834      * @param {int} iPageX the X coordinate of the mousedown or drag event
16835      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16836      */
16837     setDragElPos: function(iPageX, iPageY) {
16838         // the first time we do this, we are going to check to make sure
16839         // the element has css positioning
16840
16841         var el = this.getDragEl();
16842         this.alignElWithMouse(el, iPageX, iPageY);
16843     },
16844
16845     /**
16846      * Sets the element to the location of the mousedown or click event,
16847      * maintaining the cursor location relative to the location on the element
16848      * that was clicked.  Override this if you want to place the element in a
16849      * location other than where the cursor is.
16850      * @method alignElWithMouse
16851      * @param {HTMLElement} el the element to move
16852      * @param {int} iPageX the X coordinate of the mousedown or drag event
16853      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16854      */
16855     alignElWithMouse: function(el, iPageX, iPageY) {
16856         var oCoord = this.getTargetCoord(iPageX, iPageY);
16857         var fly = el.dom ? el : Roo.fly(el);
16858         if (!this.deltaSetXY) {
16859             var aCoord = [oCoord.x, oCoord.y];
16860             fly.setXY(aCoord);
16861             var newLeft = fly.getLeft(true);
16862             var newTop  = fly.getTop(true);
16863             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16864         } else {
16865             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16866         }
16867
16868         this.cachePosition(oCoord.x, oCoord.y);
16869         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16870         return oCoord;
16871     },
16872
16873     /**
16874      * Saves the most recent position so that we can reset the constraints and
16875      * tick marks on-demand.  We need to know this so that we can calculate the
16876      * number of pixels the element is offset from its original position.
16877      * @method cachePosition
16878      * @param iPageX the current x position (optional, this just makes it so we
16879      * don't have to look it up again)
16880      * @param iPageY the current y position (optional, this just makes it so we
16881      * don't have to look it up again)
16882      */
16883     cachePosition: function(iPageX, iPageY) {
16884         if (iPageX) {
16885             this.lastPageX = iPageX;
16886             this.lastPageY = iPageY;
16887         } else {
16888             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16889             this.lastPageX = aCoord[0];
16890             this.lastPageY = aCoord[1];
16891         }
16892     },
16893
16894     /**
16895      * Auto-scroll the window if the dragged object has been moved beyond the
16896      * visible window boundary.
16897      * @method autoScroll
16898      * @param {int} x the drag element's x position
16899      * @param {int} y the drag element's y position
16900      * @param {int} h the height of the drag element
16901      * @param {int} w the width of the drag element
16902      * @private
16903      */
16904     autoScroll: function(x, y, h, w) {
16905
16906         if (this.scroll) {
16907             // The client height
16908             var clientH = Roo.lib.Dom.getViewWidth();
16909
16910             // The client width
16911             var clientW = Roo.lib.Dom.getViewHeight();
16912
16913             // The amt scrolled down
16914             var st = this.DDM.getScrollTop();
16915
16916             // The amt scrolled right
16917             var sl = this.DDM.getScrollLeft();
16918
16919             // Location of the bottom of the element
16920             var bot = h + y;
16921
16922             // Location of the right of the element
16923             var right = w + x;
16924
16925             // The distance from the cursor to the bottom of the visible area,
16926             // adjusted so that we don't scroll if the cursor is beyond the
16927             // element drag constraints
16928             var toBot = (clientH + st - y - this.deltaY);
16929
16930             // The distance from the cursor to the right of the visible area
16931             var toRight = (clientW + sl - x - this.deltaX);
16932
16933
16934             // How close to the edge the cursor must be before we scroll
16935             // var thresh = (document.all) ? 100 : 40;
16936             var thresh = 40;
16937
16938             // How many pixels to scroll per autoscroll op.  This helps to reduce
16939             // clunky scrolling. IE is more sensitive about this ... it needs this
16940             // value to be higher.
16941             var scrAmt = (document.all) ? 80 : 30;
16942
16943             // Scroll down if we are near the bottom of the visible page and the
16944             // obj extends below the crease
16945             if ( bot > clientH && toBot < thresh ) {
16946                 window.scrollTo(sl, st + scrAmt);
16947             }
16948
16949             // Scroll up if the window is scrolled down and the top of the object
16950             // goes above the top border
16951             if ( y < st && st > 0 && y - st < thresh ) {
16952                 window.scrollTo(sl, st - scrAmt);
16953             }
16954
16955             // Scroll right if the obj is beyond the right border and the cursor is
16956             // near the border.
16957             if ( right > clientW && toRight < thresh ) {
16958                 window.scrollTo(sl + scrAmt, st);
16959             }
16960
16961             // Scroll left if the window has been scrolled to the right and the obj
16962             // extends past the left border
16963             if ( x < sl && sl > 0 && x - sl < thresh ) {
16964                 window.scrollTo(sl - scrAmt, st);
16965             }
16966         }
16967     },
16968
16969     /**
16970      * Finds the location the element should be placed if we want to move
16971      * it to where the mouse location less the click offset would place us.
16972      * @method getTargetCoord
16973      * @param {int} iPageX the X coordinate of the click
16974      * @param {int} iPageY the Y coordinate of the click
16975      * @return an object that contains the coordinates (Object.x and Object.y)
16976      * @private
16977      */
16978     getTargetCoord: function(iPageX, iPageY) {
16979
16980
16981         var x = iPageX - this.deltaX;
16982         var y = iPageY - this.deltaY;
16983
16984         if (this.constrainX) {
16985             if (x < this.minX) { x = this.minX; }
16986             if (x > this.maxX) { x = this.maxX; }
16987         }
16988
16989         if (this.constrainY) {
16990             if (y < this.minY) { y = this.minY; }
16991             if (y > this.maxY) { y = this.maxY; }
16992         }
16993
16994         x = this.getTick(x, this.xTicks);
16995         y = this.getTick(y, this.yTicks);
16996
16997
16998         return {x:x, y:y};
16999     },
17000
17001     /*
17002      * Sets up config options specific to this class. Overrides
17003      * Roo.dd.DragDrop, but all versions of this method through the
17004      * inheritance chain are called
17005      */
17006     applyConfig: function() {
17007         Roo.dd.DD.superclass.applyConfig.call(this);
17008         this.scroll = (this.config.scroll !== false);
17009     },
17010
17011     /*
17012      * Event that fires prior to the onMouseDown event.  Overrides
17013      * Roo.dd.DragDrop.
17014      */
17015     b4MouseDown: function(e) {
17016         // this.resetConstraints();
17017         this.autoOffset(e.getPageX(),
17018                             e.getPageY());
17019     },
17020
17021     /*
17022      * Event that fires prior to the onDrag event.  Overrides
17023      * Roo.dd.DragDrop.
17024      */
17025     b4Drag: function(e) {
17026         this.setDragElPos(e.getPageX(),
17027                             e.getPageY());
17028     },
17029
17030     toString: function() {
17031         return ("DD " + this.id);
17032     }
17033
17034     //////////////////////////////////////////////////////////////////////////
17035     // Debugging ygDragDrop events that can be overridden
17036     //////////////////////////////////////////////////////////////////////////
17037     /*
17038     startDrag: function(x, y) {
17039     },
17040
17041     onDrag: function(e) {
17042     },
17043
17044     onDragEnter: function(e, id) {
17045     },
17046
17047     onDragOver: function(e, id) {
17048     },
17049
17050     onDragOut: function(e, id) {
17051     },
17052
17053     onDragDrop: function(e, id) {
17054     },
17055
17056     endDrag: function(e) {
17057     }
17058
17059     */
17060
17061 });/*
17062  * Based on:
17063  * Ext JS Library 1.1.1
17064  * Copyright(c) 2006-2007, Ext JS, LLC.
17065  *
17066  * Originally Released Under LGPL - original licence link has changed is not relivant.
17067  *
17068  * Fork - LGPL
17069  * <script type="text/javascript">
17070  */
17071
17072 /**
17073  * @class Roo.dd.DDProxy
17074  * A DragDrop implementation that inserts an empty, bordered div into
17075  * the document that follows the cursor during drag operations.  At the time of
17076  * the click, the frame div is resized to the dimensions of the linked html
17077  * element, and moved to the exact location of the linked element.
17078  *
17079  * References to the "frame" element refer to the single proxy element that
17080  * was created to be dragged in place of all DDProxy elements on the
17081  * page.
17082  *
17083  * @extends Roo.dd.DD
17084  * @constructor
17085  * @param {String} id the id of the linked html element
17086  * @param {String} sGroup the group of related DragDrop objects
17087  * @param {object} config an object containing configurable attributes
17088  *                Valid properties for DDProxy in addition to those in DragDrop:
17089  *                   resizeFrame, centerFrame, dragElId
17090  */
17091 Roo.dd.DDProxy = function(id, sGroup, config) {
17092     if (id) {
17093         this.init(id, sGroup, config);
17094         this.initFrame();
17095     }
17096 };
17097
17098 /**
17099  * The default drag frame div id
17100  * @property Roo.dd.DDProxy.dragElId
17101  * @type String
17102  * @static
17103  */
17104 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17105
17106 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17107
17108     /**
17109      * By default we resize the drag frame to be the same size as the element
17110      * we want to drag (this is to get the frame effect).  We can turn it off
17111      * if we want a different behavior.
17112      * @property resizeFrame
17113      * @type boolean
17114      */
17115     resizeFrame: true,
17116
17117     /**
17118      * By default the frame is positioned exactly where the drag element is, so
17119      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17120      * you do not have constraints on the obj is to have the drag frame centered
17121      * around the cursor.  Set centerFrame to true for this effect.
17122      * @property centerFrame
17123      * @type boolean
17124      */
17125     centerFrame: false,
17126
17127     /**
17128      * Creates the proxy element if it does not yet exist
17129      * @method createFrame
17130      */
17131     createFrame: function() {
17132         var self = this;
17133         var body = document.body;
17134
17135         if (!body || !body.firstChild) {
17136             setTimeout( function() { self.createFrame(); }, 50 );
17137             return;
17138         }
17139
17140         var div = this.getDragEl();
17141
17142         if (!div) {
17143             div    = document.createElement("div");
17144             div.id = this.dragElId;
17145             var s  = div.style;
17146
17147             s.position   = "absolute";
17148             s.visibility = "hidden";
17149             s.cursor     = "move";
17150             s.border     = "2px solid #aaa";
17151             s.zIndex     = 999;
17152
17153             // appendChild can blow up IE if invoked prior to the window load event
17154             // while rendering a table.  It is possible there are other scenarios
17155             // that would cause this to happen as well.
17156             body.insertBefore(div, body.firstChild);
17157         }
17158     },
17159
17160     /**
17161      * Initialization for the drag frame element.  Must be called in the
17162      * constructor of all subclasses
17163      * @method initFrame
17164      */
17165     initFrame: function() {
17166         this.createFrame();
17167     },
17168
17169     applyConfig: function() {
17170         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17171
17172         this.resizeFrame = (this.config.resizeFrame !== false);
17173         this.centerFrame = (this.config.centerFrame);
17174         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17175     },
17176
17177     /**
17178      * Resizes the drag frame to the dimensions of the clicked object, positions
17179      * it over the object, and finally displays it
17180      * @method showFrame
17181      * @param {int} iPageX X click position
17182      * @param {int} iPageY Y click position
17183      * @private
17184      */
17185     showFrame: function(iPageX, iPageY) {
17186         var el = this.getEl();
17187         var dragEl = this.getDragEl();
17188         var s = dragEl.style;
17189
17190         this._resizeProxy();
17191
17192         if (this.centerFrame) {
17193             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17194                            Math.round(parseInt(s.height, 10)/2) );
17195         }
17196
17197         this.setDragElPos(iPageX, iPageY);
17198
17199         Roo.fly(dragEl).show();
17200     },
17201
17202     /**
17203      * The proxy is automatically resized to the dimensions of the linked
17204      * element when a drag is initiated, unless resizeFrame is set to false
17205      * @method _resizeProxy
17206      * @private
17207      */
17208     _resizeProxy: function() {
17209         if (this.resizeFrame) {
17210             var el = this.getEl();
17211             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17212         }
17213     },
17214
17215     // overrides Roo.dd.DragDrop
17216     b4MouseDown: function(e) {
17217         var x = e.getPageX();
17218         var y = e.getPageY();
17219         this.autoOffset(x, y);
17220         this.setDragElPos(x, y);
17221     },
17222
17223     // overrides Roo.dd.DragDrop
17224     b4StartDrag: function(x, y) {
17225         // show the drag frame
17226         this.showFrame(x, y);
17227     },
17228
17229     // overrides Roo.dd.DragDrop
17230     b4EndDrag: function(e) {
17231         Roo.fly(this.getDragEl()).hide();
17232     },
17233
17234     // overrides Roo.dd.DragDrop
17235     // By default we try to move the element to the last location of the frame.
17236     // This is so that the default behavior mirrors that of Roo.dd.DD.
17237     endDrag: function(e) {
17238
17239         var lel = this.getEl();
17240         var del = this.getDragEl();
17241
17242         // Show the drag frame briefly so we can get its position
17243         del.style.visibility = "";
17244
17245         this.beforeMove();
17246         // Hide the linked element before the move to get around a Safari
17247         // rendering bug.
17248         lel.style.visibility = "hidden";
17249         Roo.dd.DDM.moveToEl(lel, del);
17250         del.style.visibility = "hidden";
17251         lel.style.visibility = "";
17252
17253         this.afterDrag();
17254     },
17255
17256     beforeMove : function(){
17257
17258     },
17259
17260     afterDrag : function(){
17261
17262     },
17263
17264     toString: function() {
17265         return ("DDProxy " + this.id);
17266     }
17267
17268 });
17269 /*
17270  * Based on:
17271  * Ext JS Library 1.1.1
17272  * Copyright(c) 2006-2007, Ext JS, LLC.
17273  *
17274  * Originally Released Under LGPL - original licence link has changed is not relivant.
17275  *
17276  * Fork - LGPL
17277  * <script type="text/javascript">
17278  */
17279
17280  /**
17281  * @class Roo.dd.DDTarget
17282  * A DragDrop implementation that does not move, but can be a drop
17283  * target.  You would get the same result by simply omitting implementation
17284  * for the event callbacks, but this way we reduce the processing cost of the
17285  * event listener and the callbacks.
17286  * @extends Roo.dd.DragDrop
17287  * @constructor
17288  * @param {String} id the id of the element that is a drop target
17289  * @param {String} sGroup the group of related DragDrop objects
17290  * @param {object} config an object containing configurable attributes
17291  *                 Valid properties for DDTarget in addition to those in
17292  *                 DragDrop:
17293  *                    none
17294  */
17295 Roo.dd.DDTarget = function(id, sGroup, config) {
17296     if (id) {
17297         this.initTarget(id, sGroup, config);
17298     }
17299 };
17300
17301 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17302 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17303     toString: function() {
17304         return ("DDTarget " + this.id);
17305     }
17306 });
17307 /*
17308  * Based on:
17309  * Ext JS Library 1.1.1
17310  * Copyright(c) 2006-2007, Ext JS, LLC.
17311  *
17312  * Originally Released Under LGPL - original licence link has changed is not relivant.
17313  *
17314  * Fork - LGPL
17315  * <script type="text/javascript">
17316  */
17317  
17318
17319 /**
17320  * @class Roo.dd.ScrollManager
17321  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17322  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17323  * @singleton
17324  */
17325 Roo.dd.ScrollManager = function(){
17326     var ddm = Roo.dd.DragDropMgr;
17327     var els = {};
17328     var dragEl = null;
17329     var proc = {};
17330     
17331     var onStop = function(e){
17332         dragEl = null;
17333         clearProc();
17334     };
17335     
17336     var triggerRefresh = function(){
17337         if(ddm.dragCurrent){
17338              ddm.refreshCache(ddm.dragCurrent.groups);
17339         }
17340     };
17341     
17342     var doScroll = function(){
17343         if(ddm.dragCurrent){
17344             var dds = Roo.dd.ScrollManager;
17345             if(!dds.animate){
17346                 if(proc.el.scroll(proc.dir, dds.increment)){
17347                     triggerRefresh();
17348                 }
17349             }else{
17350                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17351             }
17352         }
17353     };
17354     
17355     var clearProc = function(){
17356         if(proc.id){
17357             clearInterval(proc.id);
17358         }
17359         proc.id = 0;
17360         proc.el = null;
17361         proc.dir = "";
17362     };
17363     
17364     var startProc = function(el, dir){
17365         clearProc();
17366         proc.el = el;
17367         proc.dir = dir;
17368         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17369     };
17370     
17371     var onFire = function(e, isDrop){
17372         if(isDrop || !ddm.dragCurrent){ return; }
17373         var dds = Roo.dd.ScrollManager;
17374         if(!dragEl || dragEl != ddm.dragCurrent){
17375             dragEl = ddm.dragCurrent;
17376             // refresh regions on drag start
17377             dds.refreshCache();
17378         }
17379         
17380         var xy = Roo.lib.Event.getXY(e);
17381         var pt = new Roo.lib.Point(xy[0], xy[1]);
17382         for(var id in els){
17383             var el = els[id], r = el._region;
17384             if(r && r.contains(pt) && el.isScrollable()){
17385                 if(r.bottom - pt.y <= dds.thresh){
17386                     if(proc.el != el){
17387                         startProc(el, "down");
17388                     }
17389                     return;
17390                 }else if(r.right - pt.x <= dds.thresh){
17391                     if(proc.el != el){
17392                         startProc(el, "left");
17393                     }
17394                     return;
17395                 }else if(pt.y - r.top <= dds.thresh){
17396                     if(proc.el != el){
17397                         startProc(el, "up");
17398                     }
17399                     return;
17400                 }else if(pt.x - r.left <= dds.thresh){
17401                     if(proc.el != el){
17402                         startProc(el, "right");
17403                     }
17404                     return;
17405                 }
17406             }
17407         }
17408         clearProc();
17409     };
17410     
17411     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17412     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17413     
17414     return {
17415         /**
17416          * Registers new overflow element(s) to auto scroll
17417          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17418          */
17419         register : function(el){
17420             if(el instanceof Array){
17421                 for(var i = 0, len = el.length; i < len; i++) {
17422                         this.register(el[i]);
17423                 }
17424             }else{
17425                 el = Roo.get(el);
17426                 els[el.id] = el;
17427             }
17428         },
17429         
17430         /**
17431          * Unregisters overflow element(s) so they are no longer scrolled
17432          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17433          */
17434         unregister : function(el){
17435             if(el instanceof Array){
17436                 for(var i = 0, len = el.length; i < len; i++) {
17437                         this.unregister(el[i]);
17438                 }
17439             }else{
17440                 el = Roo.get(el);
17441                 delete els[el.id];
17442             }
17443         },
17444         
17445         /**
17446          * The number of pixels from the edge of a container the pointer needs to be to 
17447          * trigger scrolling (defaults to 25)
17448          * @type Number
17449          */
17450         thresh : 25,
17451         
17452         /**
17453          * The number of pixels to scroll in each scroll increment (defaults to 50)
17454          * @type Number
17455          */
17456         increment : 100,
17457         
17458         /**
17459          * The frequency of scrolls in milliseconds (defaults to 500)
17460          * @type Number
17461          */
17462         frequency : 500,
17463         
17464         /**
17465          * True to animate the scroll (defaults to true)
17466          * @type Boolean
17467          */
17468         animate: true,
17469         
17470         /**
17471          * The animation duration in seconds - 
17472          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17473          * @type Number
17474          */
17475         animDuration: .4,
17476         
17477         /**
17478          * Manually trigger a cache refresh.
17479          */
17480         refreshCache : function(){
17481             for(var id in els){
17482                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17483                     els[id]._region = els[id].getRegion();
17484                 }
17485             }
17486         }
17487     };
17488 }();/*
17489  * Based on:
17490  * Ext JS Library 1.1.1
17491  * Copyright(c) 2006-2007, Ext JS, LLC.
17492  *
17493  * Originally Released Under LGPL - original licence link has changed is not relivant.
17494  *
17495  * Fork - LGPL
17496  * <script type="text/javascript">
17497  */
17498  
17499
17500 /**
17501  * @class Roo.dd.Registry
17502  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17503  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17504  * @singleton
17505  */
17506 Roo.dd.Registry = function(){
17507     var elements = {}; 
17508     var handles = {}; 
17509     var autoIdSeed = 0;
17510
17511     var getId = function(el, autogen){
17512         if(typeof el == "string"){
17513             return el;
17514         }
17515         var id = el.id;
17516         if(!id && autogen !== false){
17517             id = "roodd-" + (++autoIdSeed);
17518             el.id = id;
17519         }
17520         return id;
17521     };
17522     
17523     return {
17524     /**
17525      * Register a drag drop element
17526      * @param {String|HTMLElement} element The id or DOM node to register
17527      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17528      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17529      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17530      * populated in the data object (if applicable):
17531      * <pre>
17532 Value      Description<br />
17533 ---------  ------------------------------------------<br />
17534 handles    Array of DOM nodes that trigger dragging<br />
17535            for the element being registered<br />
17536 isHandle   True if the element passed in triggers<br />
17537            dragging itself, else false
17538 </pre>
17539      */
17540         register : function(el, data){
17541             data = data || {};
17542             if(typeof el == "string"){
17543                 el = document.getElementById(el);
17544             }
17545             data.ddel = el;
17546             elements[getId(el)] = data;
17547             if(data.isHandle !== false){
17548                 handles[data.ddel.id] = data;
17549             }
17550             if(data.handles){
17551                 var hs = data.handles;
17552                 for(var i = 0, len = hs.length; i < len; i++){
17553                         handles[getId(hs[i])] = data;
17554                 }
17555             }
17556         },
17557
17558     /**
17559      * Unregister a drag drop element
17560      * @param {String|HTMLElement}  element The id or DOM node to unregister
17561      */
17562         unregister : function(el){
17563             var id = getId(el, false);
17564             var data = elements[id];
17565             if(data){
17566                 delete elements[id];
17567                 if(data.handles){
17568                     var hs = data.handles;
17569                     for(var i = 0, len = hs.length; i < len; i++){
17570                         delete handles[getId(hs[i], false)];
17571                     }
17572                 }
17573             }
17574         },
17575
17576     /**
17577      * Returns the handle registered for a DOM Node by id
17578      * @param {String|HTMLElement} id The DOM node or id to look up
17579      * @return {Object} handle The custom handle data
17580      */
17581         getHandle : function(id){
17582             if(typeof id != "string"){ // must be element?
17583                 id = id.id;
17584             }
17585             return handles[id];
17586         },
17587
17588     /**
17589      * Returns the handle that is registered for the DOM node that is the target of the event
17590      * @param {Event} e The event
17591      * @return {Object} handle The custom handle data
17592      */
17593         getHandleFromEvent : function(e){
17594             var t = Roo.lib.Event.getTarget(e);
17595             return t ? handles[t.id] : null;
17596         },
17597
17598     /**
17599      * Returns a custom data object that is registered for a DOM node by id
17600      * @param {String|HTMLElement} id The DOM node or id to look up
17601      * @return {Object} data The custom data
17602      */
17603         getTarget : function(id){
17604             if(typeof id != "string"){ // must be element?
17605                 id = id.id;
17606             }
17607             return elements[id];
17608         },
17609
17610     /**
17611      * Returns a custom data object that is registered for the DOM node that is the target of the event
17612      * @param {Event} e The event
17613      * @return {Object} data The custom data
17614      */
17615         getTargetFromEvent : function(e){
17616             var t = Roo.lib.Event.getTarget(e);
17617             return t ? elements[t.id] || handles[t.id] : null;
17618         }
17619     };
17620 }();/*
17621  * Based on:
17622  * Ext JS Library 1.1.1
17623  * Copyright(c) 2006-2007, Ext JS, LLC.
17624  *
17625  * Originally Released Under LGPL - original licence link has changed is not relivant.
17626  *
17627  * Fork - LGPL
17628  * <script type="text/javascript">
17629  */
17630  
17631
17632 /**
17633  * @class Roo.dd.StatusProxy
17634  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17635  * default drag proxy used by all Roo.dd components.
17636  * @constructor
17637  * @param {Object} config
17638  */
17639 Roo.dd.StatusProxy = function(config){
17640     Roo.apply(this, config);
17641     this.id = this.id || Roo.id();
17642     this.el = new Roo.Layer({
17643         dh: {
17644             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17645                 {tag: "div", cls: "x-dd-drop-icon"},
17646                 {tag: "div", cls: "x-dd-drag-ghost"}
17647             ]
17648         }, 
17649         shadow: !config || config.shadow !== false
17650     });
17651     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17652     this.dropStatus = this.dropNotAllowed;
17653 };
17654
17655 Roo.dd.StatusProxy.prototype = {
17656     /**
17657      * @cfg {String} dropAllowed
17658      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17659      */
17660     dropAllowed : "x-dd-drop-ok",
17661     /**
17662      * @cfg {String} dropNotAllowed
17663      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17664      */
17665     dropNotAllowed : "x-dd-drop-nodrop",
17666
17667     /**
17668      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17669      * over the current target element.
17670      * @param {String} cssClass The css class for the new drop status indicator image
17671      */
17672     setStatus : function(cssClass){
17673         cssClass = cssClass || this.dropNotAllowed;
17674         if(this.dropStatus != cssClass){
17675             this.el.replaceClass(this.dropStatus, cssClass);
17676             this.dropStatus = cssClass;
17677         }
17678     },
17679
17680     /**
17681      * Resets the status indicator to the default dropNotAllowed value
17682      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17683      */
17684     reset : function(clearGhost){
17685         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17686         this.dropStatus = this.dropNotAllowed;
17687         if(clearGhost){
17688             this.ghost.update("");
17689         }
17690     },
17691
17692     /**
17693      * Updates the contents of the ghost element
17694      * @param {String} html The html that will replace the current innerHTML of the ghost element
17695      */
17696     update : function(html){
17697         if(typeof html == "string"){
17698             this.ghost.update(html);
17699         }else{
17700             this.ghost.update("");
17701             html.style.margin = "0";
17702             this.ghost.dom.appendChild(html);
17703         }
17704         // ensure float = none set?? cant remember why though.
17705         var el = this.ghost.dom.firstChild;
17706                 if(el){
17707                         Roo.fly(el).setStyle('float', 'none');
17708                 }
17709     },
17710     
17711     /**
17712      * Returns the underlying proxy {@link Roo.Layer}
17713      * @return {Roo.Layer} el
17714     */
17715     getEl : function(){
17716         return this.el;
17717     },
17718
17719     /**
17720      * Returns the ghost element
17721      * @return {Roo.Element} el
17722      */
17723     getGhost : function(){
17724         return this.ghost;
17725     },
17726
17727     /**
17728      * Hides the proxy
17729      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17730      */
17731     hide : function(clear){
17732         this.el.hide();
17733         if(clear){
17734             this.reset(true);
17735         }
17736     },
17737
17738     /**
17739      * Stops the repair animation if it's currently running
17740      */
17741     stop : function(){
17742         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17743             this.anim.stop();
17744         }
17745     },
17746
17747     /**
17748      * Displays this proxy
17749      */
17750     show : function(){
17751         this.el.show();
17752     },
17753
17754     /**
17755      * Force the Layer to sync its shadow and shim positions to the element
17756      */
17757     sync : function(){
17758         this.el.sync();
17759     },
17760
17761     /**
17762      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17763      * invalid drop operation by the item being dragged.
17764      * @param {Array} xy The XY position of the element ([x, y])
17765      * @param {Function} callback The function to call after the repair is complete
17766      * @param {Object} scope The scope in which to execute the callback
17767      */
17768     repair : function(xy, callback, scope){
17769         this.callback = callback;
17770         this.scope = scope;
17771         if(xy && this.animRepair !== false){
17772             this.el.addClass("x-dd-drag-repair");
17773             this.el.hideUnders(true);
17774             this.anim = this.el.shift({
17775                 duration: this.repairDuration || .5,
17776                 easing: 'easeOut',
17777                 xy: xy,
17778                 stopFx: true,
17779                 callback: this.afterRepair,
17780                 scope: this
17781             });
17782         }else{
17783             this.afterRepair();
17784         }
17785     },
17786
17787     // private
17788     afterRepair : function(){
17789         this.hide(true);
17790         if(typeof this.callback == "function"){
17791             this.callback.call(this.scope || this);
17792         }
17793         this.callback = null;
17794         this.scope = null;
17795     }
17796 };/*
17797  * Based on:
17798  * Ext JS Library 1.1.1
17799  * Copyright(c) 2006-2007, Ext JS, LLC.
17800  *
17801  * Originally Released Under LGPL - original licence link has changed is not relivant.
17802  *
17803  * Fork - LGPL
17804  * <script type="text/javascript">
17805  */
17806
17807 /**
17808  * @class Roo.dd.DragSource
17809  * @extends Roo.dd.DDProxy
17810  * A simple class that provides the basic implementation needed to make any element draggable.
17811  * @constructor
17812  * @param {String/HTMLElement/Element} el The container element
17813  * @param {Object} config
17814  */
17815 Roo.dd.DragSource = function(el, config){
17816     this.el = Roo.get(el);
17817     this.dragData = {};
17818     
17819     Roo.apply(this, config);
17820     
17821     if(!this.proxy){
17822         this.proxy = new Roo.dd.StatusProxy();
17823     }
17824
17825     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17826           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17827     
17828     this.dragging = false;
17829 };
17830
17831 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17832     /**
17833      * @cfg {String} dropAllowed
17834      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17835      */
17836     dropAllowed : "x-dd-drop-ok",
17837     /**
17838      * @cfg {String} dropNotAllowed
17839      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17840      */
17841     dropNotAllowed : "x-dd-drop-nodrop",
17842
17843     /**
17844      * Returns the data object associated with this drag source
17845      * @return {Object} data An object containing arbitrary data
17846      */
17847     getDragData : function(e){
17848         return this.dragData;
17849     },
17850
17851     // private
17852     onDragEnter : function(e, id){
17853         var target = Roo.dd.DragDropMgr.getDDById(id);
17854         this.cachedTarget = target;
17855         if(this.beforeDragEnter(target, e, id) !== false){
17856             if(target.isNotifyTarget){
17857                 var status = target.notifyEnter(this, e, this.dragData);
17858                 this.proxy.setStatus(status);
17859             }else{
17860                 this.proxy.setStatus(this.dropAllowed);
17861             }
17862             
17863             if(this.afterDragEnter){
17864                 /**
17865                  * An empty function by default, but provided so that you can perform a custom action
17866                  * when the dragged item enters the drop target by providing an implementation.
17867                  * @param {Roo.dd.DragDrop} target The drop target
17868                  * @param {Event} e The event object
17869                  * @param {String} id The id of the dragged element
17870                  * @method afterDragEnter
17871                  */
17872                 this.afterDragEnter(target, e, id);
17873             }
17874         }
17875     },
17876
17877     /**
17878      * An empty function by default, but provided so that you can perform a custom action
17879      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17880      * @param {Roo.dd.DragDrop} target The drop target
17881      * @param {Event} e The event object
17882      * @param {String} id The id of the dragged element
17883      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17884      */
17885     beforeDragEnter : function(target, e, id){
17886         return true;
17887     },
17888
17889     // private
17890     alignElWithMouse: function() {
17891         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17892         this.proxy.sync();
17893     },
17894
17895     // private
17896     onDragOver : function(e, id){
17897         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17898         if(this.beforeDragOver(target, e, id) !== false){
17899             if(target.isNotifyTarget){
17900                 var status = target.notifyOver(this, e, this.dragData);
17901                 this.proxy.setStatus(status);
17902             }
17903
17904             if(this.afterDragOver){
17905                 /**
17906                  * An empty function by default, but provided so that you can perform a custom action
17907                  * while the dragged item is over the drop target by providing an implementation.
17908                  * @param {Roo.dd.DragDrop} target The drop target
17909                  * @param {Event} e The event object
17910                  * @param {String} id The id of the dragged element
17911                  * @method afterDragOver
17912                  */
17913                 this.afterDragOver(target, e, id);
17914             }
17915         }
17916     },
17917
17918     /**
17919      * An empty function by default, but provided so that you can perform a custom action
17920      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17921      * @param {Roo.dd.DragDrop} target The drop target
17922      * @param {Event} e The event object
17923      * @param {String} id The id of the dragged element
17924      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17925      */
17926     beforeDragOver : function(target, e, id){
17927         return true;
17928     },
17929
17930     // private
17931     onDragOut : function(e, id){
17932         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17933         if(this.beforeDragOut(target, e, id) !== false){
17934             if(target.isNotifyTarget){
17935                 target.notifyOut(this, e, this.dragData);
17936             }
17937             this.proxy.reset();
17938             if(this.afterDragOut){
17939                 /**
17940                  * An empty function by default, but provided so that you can perform a custom action
17941                  * after the dragged item is dragged out of the target without dropping.
17942                  * @param {Roo.dd.DragDrop} target The drop target
17943                  * @param {Event} e The event object
17944                  * @param {String} id The id of the dragged element
17945                  * @method afterDragOut
17946                  */
17947                 this.afterDragOut(target, e, id);
17948             }
17949         }
17950         this.cachedTarget = null;
17951     },
17952
17953     /**
17954      * An empty function by default, but provided so that you can perform a custom action before the dragged
17955      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17956      * @param {Roo.dd.DragDrop} target The drop target
17957      * @param {Event} e The event object
17958      * @param {String} id The id of the dragged element
17959      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17960      */
17961     beforeDragOut : function(target, e, id){
17962         return true;
17963     },
17964     
17965     // private
17966     onDragDrop : function(e, id){
17967         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17968         if(this.beforeDragDrop(target, e, id) !== false){
17969             if(target.isNotifyTarget){
17970                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17971                     this.onValidDrop(target, e, id);
17972                 }else{
17973                     this.onInvalidDrop(target, e, id);
17974                 }
17975             }else{
17976                 this.onValidDrop(target, e, id);
17977             }
17978             
17979             if(this.afterDragDrop){
17980                 /**
17981                  * An empty function by default, but provided so that you can perform a custom action
17982                  * after a valid drag drop has occurred by providing an implementation.
17983                  * @param {Roo.dd.DragDrop} target The drop target
17984                  * @param {Event} e The event object
17985                  * @param {String} id The id of the dropped element
17986                  * @method afterDragDrop
17987                  */
17988                 this.afterDragDrop(target, e, id);
17989             }
17990         }
17991         delete this.cachedTarget;
17992     },
17993
17994     /**
17995      * An empty function by default, but provided so that you can perform a custom action before the dragged
17996      * item is dropped onto the target and optionally cancel the onDragDrop.
17997      * @param {Roo.dd.DragDrop} target The drop target
17998      * @param {Event} e The event object
17999      * @param {String} id The id of the dragged element
18000      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18001      */
18002     beforeDragDrop : function(target, e, id){
18003         return true;
18004     },
18005
18006     // private
18007     onValidDrop : function(target, e, id){
18008         this.hideProxy();
18009         if(this.afterValidDrop){
18010             /**
18011              * An empty function by default, but provided so that you can perform a custom action
18012              * after a valid drop has occurred by providing an implementation.
18013              * @param {Object} target The target DD 
18014              * @param {Event} e The event object
18015              * @param {String} id The id of the dropped element
18016              * @method afterInvalidDrop
18017              */
18018             this.afterValidDrop(target, e, id);
18019         }
18020     },
18021
18022     // private
18023     getRepairXY : function(e, data){
18024         return this.el.getXY();  
18025     },
18026
18027     // private
18028     onInvalidDrop : function(target, e, id){
18029         this.beforeInvalidDrop(target, e, id);
18030         if(this.cachedTarget){
18031             if(this.cachedTarget.isNotifyTarget){
18032                 this.cachedTarget.notifyOut(this, e, this.dragData);
18033             }
18034             this.cacheTarget = null;
18035         }
18036         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18037
18038         if(this.afterInvalidDrop){
18039             /**
18040              * An empty function by default, but provided so that you can perform a custom action
18041              * after an invalid drop has occurred by providing an implementation.
18042              * @param {Event} e The event object
18043              * @param {String} id The id of the dropped element
18044              * @method afterInvalidDrop
18045              */
18046             this.afterInvalidDrop(e, id);
18047         }
18048     },
18049
18050     // private
18051     afterRepair : function(){
18052         if(Roo.enableFx){
18053             this.el.highlight(this.hlColor || "c3daf9");
18054         }
18055         this.dragging = false;
18056     },
18057
18058     /**
18059      * An empty function by default, but provided so that you can perform a custom action after an invalid
18060      * drop has occurred.
18061      * @param {Roo.dd.DragDrop} target The drop target
18062      * @param {Event} e The event object
18063      * @param {String} id The id of the dragged element
18064      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18065      */
18066     beforeInvalidDrop : function(target, e, id){
18067         return true;
18068     },
18069
18070     // private
18071     handleMouseDown : function(e){
18072         if(this.dragging) {
18073             return;
18074         }
18075         var data = this.getDragData(e);
18076         if(data && this.onBeforeDrag(data, e) !== false){
18077             this.dragData = data;
18078             this.proxy.stop();
18079             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18080         } 
18081     },
18082
18083     /**
18084      * An empty function by default, but provided so that you can perform a custom action before the initial
18085      * drag event begins and optionally cancel it.
18086      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18087      * @param {Event} e The event object
18088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18089      */
18090     onBeforeDrag : function(data, e){
18091         return true;
18092     },
18093
18094     /**
18095      * An empty function by default, but provided so that you can perform a custom action once the initial
18096      * drag event has begun.  The drag cannot be canceled from this function.
18097      * @param {Number} x The x position of the click on the dragged object
18098      * @param {Number} y The y position of the click on the dragged object
18099      */
18100     onStartDrag : Roo.emptyFn,
18101
18102     // private - YUI override
18103     startDrag : function(x, y){
18104         this.proxy.reset();
18105         this.dragging = true;
18106         this.proxy.update("");
18107         this.onInitDrag(x, y);
18108         this.proxy.show();
18109     },
18110
18111     // private
18112     onInitDrag : function(x, y){
18113         var clone = this.el.dom.cloneNode(true);
18114         clone.id = Roo.id(); // prevent duplicate ids
18115         this.proxy.update(clone);
18116         this.onStartDrag(x, y);
18117         return true;
18118     },
18119
18120     /**
18121      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18122      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18123      */
18124     getProxy : function(){
18125         return this.proxy;  
18126     },
18127
18128     /**
18129      * Hides the drag source's {@link Roo.dd.StatusProxy}
18130      */
18131     hideProxy : function(){
18132         this.proxy.hide();  
18133         this.proxy.reset(true);
18134         this.dragging = false;
18135     },
18136
18137     // private
18138     triggerCacheRefresh : function(){
18139         Roo.dd.DDM.refreshCache(this.groups);
18140     },
18141
18142     // private - override to prevent hiding
18143     b4EndDrag: function(e) {
18144     },
18145
18146     // private - override to prevent moving
18147     endDrag : function(e){
18148         this.onEndDrag(this.dragData, e);
18149     },
18150
18151     // private
18152     onEndDrag : function(data, e){
18153     },
18154     
18155     // private - pin to cursor
18156     autoOffset : function(x, y) {
18157         this.setDelta(-12, -20);
18158     }    
18159 });/*
18160  * Based on:
18161  * Ext JS Library 1.1.1
18162  * Copyright(c) 2006-2007, Ext JS, LLC.
18163  *
18164  * Originally Released Under LGPL - original licence link has changed is not relivant.
18165  *
18166  * Fork - LGPL
18167  * <script type="text/javascript">
18168  */
18169
18170
18171 /**
18172  * @class Roo.dd.DropTarget
18173  * @extends Roo.dd.DDTarget
18174  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18175  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18176  * @constructor
18177  * @param {String/HTMLElement/Element} el The container element
18178  * @param {Object} config
18179  */
18180 Roo.dd.DropTarget = function(el, config){
18181     this.el = Roo.get(el);
18182     
18183     Roo.apply(this, config);
18184     
18185     if(this.containerScroll){
18186         Roo.dd.ScrollManager.register(this.el);
18187     }
18188     
18189     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18190           {isTarget: true});
18191
18192 };
18193
18194 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18195     /**
18196      * @cfg {String} overClass
18197      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18198      */
18199     /**
18200      * @cfg {String} dropAllowed
18201      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18202      */
18203     dropAllowed : "x-dd-drop-ok",
18204     /**
18205      * @cfg {String} dropNotAllowed
18206      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18207      */
18208     dropNotAllowed : "x-dd-drop-nodrop",
18209
18210     // private
18211     isTarget : true,
18212
18213     // private
18214     isNotifyTarget : true,
18215
18216     /**
18217      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18218      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18219      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18220      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18221      * @param {Event} e The event
18222      * @param {Object} data An object containing arbitrary data supplied by the drag source
18223      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18224      * underlying {@link Roo.dd.StatusProxy} can be updated
18225      */
18226     notifyEnter : function(dd, e, data){
18227         if(this.overClass){
18228             this.el.addClass(this.overClass);
18229         }
18230         return this.dropAllowed;
18231     },
18232
18233     /**
18234      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18235      * This method will be called on every mouse movement while the drag source is over the drop target.
18236      * This default implementation simply returns the dropAllowed config value.
18237      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18238      * @param {Event} e The event
18239      * @param {Object} data An object containing arbitrary data supplied by the drag source
18240      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18241      * underlying {@link Roo.dd.StatusProxy} can be updated
18242      */
18243     notifyOver : function(dd, e, data){
18244         return this.dropAllowed;
18245     },
18246
18247     /**
18248      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18249      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18250      * overClass (if any) from the drop element.
18251      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18252      * @param {Event} e The event
18253      * @param {Object} data An object containing arbitrary data supplied by the drag source
18254      */
18255     notifyOut : function(dd, e, data){
18256         if(this.overClass){
18257             this.el.removeClass(this.overClass);
18258         }
18259     },
18260
18261     /**
18262      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18263      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18264      * implementation that does something to process the drop event and returns true so that the drag source's
18265      * repair action does not run.
18266      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18267      * @param {Event} e The event
18268      * @param {Object} data An object containing arbitrary data supplied by the drag source
18269      * @return {Boolean} True if the drop was valid, else false
18270      */
18271     notifyDrop : function(dd, e, data){
18272         return false;
18273     }
18274 });/*
18275  * Based on:
18276  * Ext JS Library 1.1.1
18277  * Copyright(c) 2006-2007, Ext JS, LLC.
18278  *
18279  * Originally Released Under LGPL - original licence link has changed is not relivant.
18280  *
18281  * Fork - LGPL
18282  * <script type="text/javascript">
18283  */
18284
18285
18286 /**
18287  * @class Roo.dd.DragZone
18288  * @extends Roo.dd.DragSource
18289  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18290  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18291  * @constructor
18292  * @param {String/HTMLElement/Element} el The container element
18293  * @param {Object} config
18294  */
18295 Roo.dd.DragZone = function(el, config){
18296     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18297     if(this.containerScroll){
18298         Roo.dd.ScrollManager.register(this.el);
18299     }
18300 };
18301
18302 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18303     /**
18304      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18305      * for auto scrolling during drag operations.
18306      */
18307     /**
18308      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18309      * method after a failed drop (defaults to "c3daf9" - light blue)
18310      */
18311
18312     /**
18313      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18314      * for a valid target to drag based on the mouse down. Override this method
18315      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18316      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18317      * @param {EventObject} e The mouse down event
18318      * @return {Object} The dragData
18319      */
18320     getDragData : function(e){
18321         return Roo.dd.Registry.getHandleFromEvent(e);
18322     },
18323     
18324     /**
18325      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18326      * this.dragData.ddel
18327      * @param {Number} x The x position of the click on the dragged object
18328      * @param {Number} y The y position of the click on the dragged object
18329      * @return {Boolean} true to continue the drag, false to cancel
18330      */
18331     onInitDrag : function(x, y){
18332         this.proxy.update(this.dragData.ddel.cloneNode(true));
18333         this.onStartDrag(x, y);
18334         return true;
18335     },
18336     
18337     /**
18338      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18339      */
18340     afterRepair : function(){
18341         if(Roo.enableFx){
18342             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18343         }
18344         this.dragging = false;
18345     },
18346
18347     /**
18348      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18349      * the XY of this.dragData.ddel
18350      * @param {EventObject} e The mouse up event
18351      * @return {Array} The xy location (e.g. [100, 200])
18352      */
18353     getRepairXY : function(e){
18354         return Roo.Element.fly(this.dragData.ddel).getXY();  
18355     }
18356 });/*
18357  * Based on:
18358  * Ext JS Library 1.1.1
18359  * Copyright(c) 2006-2007, Ext JS, LLC.
18360  *
18361  * Originally Released Under LGPL - original licence link has changed is not relivant.
18362  *
18363  * Fork - LGPL
18364  * <script type="text/javascript">
18365  */
18366 /**
18367  * @class Roo.dd.DropZone
18368  * @extends Roo.dd.DropTarget
18369  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18370  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18371  * @constructor
18372  * @param {String/HTMLElement/Element} el The container element
18373  * @param {Object} config
18374  */
18375 Roo.dd.DropZone = function(el, config){
18376     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18377 };
18378
18379 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18380     /**
18381      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18382      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18383      * provide your own custom lookup.
18384      * @param {Event} e The event
18385      * @return {Object} data The custom data
18386      */
18387     getTargetFromEvent : function(e){
18388         return Roo.dd.Registry.getTargetFromEvent(e);
18389     },
18390
18391     /**
18392      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18393      * that it has registered.  This method has no default implementation and should be overridden to provide
18394      * node-specific processing if necessary.
18395      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18396      * {@link #getTargetFromEvent} for this node)
18397      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18398      * @param {Event} e The event
18399      * @param {Object} data An object containing arbitrary data supplied by the drag source
18400      */
18401     onNodeEnter : function(n, dd, e, data){
18402         
18403     },
18404
18405     /**
18406      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18407      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18408      * overridden to provide the proper feedback.
18409      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18410      * {@link #getTargetFromEvent} for this node)
18411      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18412      * @param {Event} e The event
18413      * @param {Object} data An object containing arbitrary data supplied by the drag source
18414      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18415      * underlying {@link Roo.dd.StatusProxy} can be updated
18416      */
18417     onNodeOver : function(n, dd, e, data){
18418         return this.dropAllowed;
18419     },
18420
18421     /**
18422      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18423      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18424      * node-specific processing if necessary.
18425      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18426      * {@link #getTargetFromEvent} for this node)
18427      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18428      * @param {Event} e The event
18429      * @param {Object} data An object containing arbitrary data supplied by the drag source
18430      */
18431     onNodeOut : function(n, dd, e, data){
18432         
18433     },
18434
18435     /**
18436      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18437      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18438      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18439      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18440      * {@link #getTargetFromEvent} for this node)
18441      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18442      * @param {Event} e The event
18443      * @param {Object} data An object containing arbitrary data supplied by the drag source
18444      * @return {Boolean} True if the drop was valid, else false
18445      */
18446     onNodeDrop : function(n, dd, e, data){
18447         return false;
18448     },
18449
18450     /**
18451      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18452      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18453      * it should be overridden to provide the proper feedback if necessary.
18454      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18455      * @param {Event} e The event
18456      * @param {Object} data An object containing arbitrary data supplied by the drag source
18457      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18458      * underlying {@link Roo.dd.StatusProxy} can be updated
18459      */
18460     onContainerOver : function(dd, e, data){
18461         return this.dropNotAllowed;
18462     },
18463
18464     /**
18465      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18466      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18467      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18468      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18469      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18470      * @param {Event} e The event
18471      * @param {Object} data An object containing arbitrary data supplied by the drag source
18472      * @return {Boolean} True if the drop was valid, else false
18473      */
18474     onContainerDrop : function(dd, e, data){
18475         return false;
18476     },
18477
18478     /**
18479      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18480      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18481      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18482      * you should override this method and provide a custom implementation.
18483      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18484      * @param {Event} e The event
18485      * @param {Object} data An object containing arbitrary data supplied by the drag source
18486      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18487      * underlying {@link Roo.dd.StatusProxy} can be updated
18488      */
18489     notifyEnter : function(dd, e, data){
18490         return this.dropNotAllowed;
18491     },
18492
18493     /**
18494      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18495      * This method will be called on every mouse movement while the drag source is over the drop zone.
18496      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18497      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18498      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18499      * registered node, it will call {@link #onContainerOver}.
18500      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18501      * @param {Event} e The event
18502      * @param {Object} data An object containing arbitrary data supplied by the drag source
18503      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18504      * underlying {@link Roo.dd.StatusProxy} can be updated
18505      */
18506     notifyOver : function(dd, e, data){
18507         var n = this.getTargetFromEvent(e);
18508         if(!n){ // not over valid drop target
18509             if(this.lastOverNode){
18510                 this.onNodeOut(this.lastOverNode, dd, e, data);
18511                 this.lastOverNode = null;
18512             }
18513             return this.onContainerOver(dd, e, data);
18514         }
18515         if(this.lastOverNode != n){
18516             if(this.lastOverNode){
18517                 this.onNodeOut(this.lastOverNode, dd, e, data);
18518             }
18519             this.onNodeEnter(n, dd, e, data);
18520             this.lastOverNode = n;
18521         }
18522         return this.onNodeOver(n, dd, e, data);
18523     },
18524
18525     /**
18526      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18527      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18528      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18529      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18530      * @param {Event} e The event
18531      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18532      */
18533     notifyOut : function(dd, e, data){
18534         if(this.lastOverNode){
18535             this.onNodeOut(this.lastOverNode, dd, e, data);
18536             this.lastOverNode = null;
18537         }
18538     },
18539
18540     /**
18541      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18542      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18543      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18544      * otherwise it will call {@link #onContainerDrop}.
18545      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18546      * @param {Event} e The event
18547      * @param {Object} data An object containing arbitrary data supplied by the drag source
18548      * @return {Boolean} True if the drop was valid, else false
18549      */
18550     notifyDrop : function(dd, e, data){
18551         if(this.lastOverNode){
18552             this.onNodeOut(this.lastOverNode, dd, e, data);
18553             this.lastOverNode = null;
18554         }
18555         var n = this.getTargetFromEvent(e);
18556         return n ?
18557             this.onNodeDrop(n, dd, e, data) :
18558             this.onContainerDrop(dd, e, data);
18559     },
18560
18561     // private
18562     triggerCacheRefresh : function(){
18563         Roo.dd.DDM.refreshCache(this.groups);
18564     }  
18565 });/*
18566  * Based on:
18567  * Ext JS Library 1.1.1
18568  * Copyright(c) 2006-2007, Ext JS, LLC.
18569  *
18570  * Originally Released Under LGPL - original licence link has changed is not relivant.
18571  *
18572  * Fork - LGPL
18573  * <script type="text/javascript">
18574  */
18575
18576
18577 /**
18578  * @class Roo.data.SortTypes
18579  * @singleton
18580  * Defines the default sorting (casting?) comparison functions used when sorting data.
18581  */
18582 Roo.data.SortTypes = {
18583     /**
18584      * Default sort that does nothing
18585      * @param {Mixed} s The value being converted
18586      * @return {Mixed} The comparison value
18587      */
18588     none : function(s){
18589         return s;
18590     },
18591     
18592     /**
18593      * The regular expression used to strip tags
18594      * @type {RegExp}
18595      * @property
18596      */
18597     stripTagsRE : /<\/?[^>]+>/gi,
18598     
18599     /**
18600      * Strips all HTML tags to sort on text only
18601      * @param {Mixed} s The value being converted
18602      * @return {String} The comparison value
18603      */
18604     asText : function(s){
18605         return String(s).replace(this.stripTagsRE, "");
18606     },
18607     
18608     /**
18609      * Strips all HTML tags to sort on text only - Case insensitive
18610      * @param {Mixed} s The value being converted
18611      * @return {String} The comparison value
18612      */
18613     asUCText : function(s){
18614         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18615     },
18616     
18617     /**
18618      * Case insensitive string
18619      * @param {Mixed} s The value being converted
18620      * @return {String} The comparison value
18621      */
18622     asUCString : function(s) {
18623         return String(s).toUpperCase();
18624     },
18625     
18626     /**
18627      * Date sorting
18628      * @param {Mixed} s The value being converted
18629      * @return {Number} The comparison value
18630      */
18631     asDate : function(s) {
18632         if(!s){
18633             return 0;
18634         }
18635         if(s instanceof Date){
18636             return s.getTime();
18637         }
18638         return Date.parse(String(s));
18639     },
18640     
18641     /**
18642      * Float sorting
18643      * @param {Mixed} s The value being converted
18644      * @return {Float} The comparison value
18645      */
18646     asFloat : function(s) {
18647         var val = parseFloat(String(s).replace(/,/g, ""));
18648         if(isNaN(val)) val = 0;
18649         return val;
18650     },
18651     
18652     /**
18653      * Integer sorting
18654      * @param {Mixed} s The value being converted
18655      * @return {Number} The comparison value
18656      */
18657     asInt : function(s) {
18658         var val = parseInt(String(s).replace(/,/g, ""));
18659         if(isNaN(val)) val = 0;
18660         return val;
18661     }
18662 };/*
18663  * Based on:
18664  * Ext JS Library 1.1.1
18665  * Copyright(c) 2006-2007, Ext JS, LLC.
18666  *
18667  * Originally Released Under LGPL - original licence link has changed is not relivant.
18668  *
18669  * Fork - LGPL
18670  * <script type="text/javascript">
18671  */
18672
18673 /**
18674 * @class Roo.data.Record
18675  * Instances of this class encapsulate both record <em>definition</em> information, and record
18676  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18677  * to access Records cached in an {@link Roo.data.Store} object.<br>
18678  * <p>
18679  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18680  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18681  * objects.<br>
18682  * <p>
18683  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18684  * @constructor
18685  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18686  * {@link #create}. The parameters are the same.
18687  * @param {Array} data An associative Array of data values keyed by the field name.
18688  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18689  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18690  * not specified an integer id is generated.
18691  */
18692 Roo.data.Record = function(data, id){
18693     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18694     this.data = data;
18695 };
18696
18697 /**
18698  * Generate a constructor for a specific record layout.
18699  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18700  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18701  * Each field definition object may contain the following properties: <ul>
18702  * <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,
18703  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18704  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18705  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18706  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18707  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18708  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18709  * this may be omitted.</p></li>
18710  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18711  * <ul><li>auto (Default, implies no conversion)</li>
18712  * <li>string</li>
18713  * <li>int</li>
18714  * <li>float</li>
18715  * <li>boolean</li>
18716  * <li>date</li></ul></p></li>
18717  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18718  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18719  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18720  * by the Reader into an object that will be stored in the Record. It is passed the
18721  * following parameters:<ul>
18722  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18723  * </ul></p></li>
18724  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18725  * </ul>
18726  * <br>usage:<br><pre><code>
18727 var TopicRecord = Roo.data.Record.create(
18728     {name: 'title', mapping: 'topic_title'},
18729     {name: 'author', mapping: 'username'},
18730     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18731     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18732     {name: 'lastPoster', mapping: 'user2'},
18733     {name: 'excerpt', mapping: 'post_text'}
18734 );
18735
18736 var myNewRecord = new TopicRecord({
18737     title: 'Do my job please',
18738     author: 'noobie',
18739     totalPosts: 1,
18740     lastPost: new Date(),
18741     lastPoster: 'Animal',
18742     excerpt: 'No way dude!'
18743 });
18744 myStore.add(myNewRecord);
18745 </code></pre>
18746  * @method create
18747  * @static
18748  */
18749 Roo.data.Record.create = function(o){
18750     var f = function(){
18751         f.superclass.constructor.apply(this, arguments);
18752     };
18753     Roo.extend(f, Roo.data.Record);
18754     var p = f.prototype;
18755     p.fields = new Roo.util.MixedCollection(false, function(field){
18756         return field.name;
18757     });
18758     for(var i = 0, len = o.length; i < len; i++){
18759         p.fields.add(new Roo.data.Field(o[i]));
18760     }
18761     f.getField = function(name){
18762         return p.fields.get(name);  
18763     };
18764     return f;
18765 };
18766
18767 Roo.data.Record.AUTO_ID = 1000;
18768 Roo.data.Record.EDIT = 'edit';
18769 Roo.data.Record.REJECT = 'reject';
18770 Roo.data.Record.COMMIT = 'commit';
18771
18772 Roo.data.Record.prototype = {
18773     /**
18774      * Readonly flag - true if this record has been modified.
18775      * @type Boolean
18776      */
18777     dirty : false,
18778     editing : false,
18779     error: null,
18780     modified: null,
18781
18782     // private
18783     join : function(store){
18784         this.store = store;
18785     },
18786
18787     /**
18788      * Set the named field to the specified value.
18789      * @param {String} name The name of the field to set.
18790      * @param {Object} value The value to set the field to.
18791      */
18792     set : function(name, value){
18793         if(this.data[name] == value){
18794             return;
18795         }
18796         this.dirty = true;
18797         if(!this.modified){
18798             this.modified = {};
18799         }
18800         if(typeof this.modified[name] == 'undefined'){
18801             this.modified[name] = this.data[name];
18802         }
18803         this.data[name] = value;
18804         if(!this.editing){
18805             this.store.afterEdit(this);
18806         }       
18807     },
18808
18809     /**
18810      * Get the value of the named field.
18811      * @param {String} name The name of the field to get the value of.
18812      * @return {Object} The value of the field.
18813      */
18814     get : function(name){
18815         return this.data[name]; 
18816     },
18817
18818     // private
18819     beginEdit : function(){
18820         this.editing = true;
18821         this.modified = {}; 
18822     },
18823
18824     // private
18825     cancelEdit : function(){
18826         this.editing = false;
18827         delete this.modified;
18828     },
18829
18830     // private
18831     endEdit : function(){
18832         this.editing = false;
18833         if(this.dirty && this.store){
18834             this.store.afterEdit(this);
18835         }
18836     },
18837
18838     /**
18839      * Usually called by the {@link Roo.data.Store} which owns the Record.
18840      * Rejects all changes made to the Record since either creation, or the last commit operation.
18841      * Modified fields are reverted to their original values.
18842      * <p>
18843      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18844      * of reject operations.
18845      */
18846     reject : function(){
18847         var m = this.modified;
18848         for(var n in m){
18849             if(typeof m[n] != "function"){
18850                 this.data[n] = m[n];
18851             }
18852         }
18853         this.dirty = false;
18854         delete this.modified;
18855         this.editing = false;
18856         if(this.store){
18857             this.store.afterReject(this);
18858         }
18859     },
18860
18861     /**
18862      * Usually called by the {@link Roo.data.Store} which owns the Record.
18863      * Commits all changes made to the Record since either creation, or the last commit operation.
18864      * <p>
18865      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18866      * of commit operations.
18867      */
18868     commit : function(){
18869         this.dirty = false;
18870         delete this.modified;
18871         this.editing = false;
18872         if(this.store){
18873             this.store.afterCommit(this);
18874         }
18875     },
18876
18877     // private
18878     hasError : function(){
18879         return this.error != null;
18880     },
18881
18882     // private
18883     clearError : function(){
18884         this.error = null;
18885     },
18886
18887     /**
18888      * Creates a copy of this record.
18889      * @param {String} id (optional) A new record id if you don't want to use this record's id
18890      * @return {Record}
18891      */
18892     copy : function(newId) {
18893         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18894     }
18895 };/*
18896  * Based on:
18897  * Ext JS Library 1.1.1
18898  * Copyright(c) 2006-2007, Ext JS, LLC.
18899  *
18900  * Originally Released Under LGPL - original licence link has changed is not relivant.
18901  *
18902  * Fork - LGPL
18903  * <script type="text/javascript">
18904  */
18905
18906
18907
18908 /**
18909  * @class Roo.data.Store
18910  * @extends Roo.util.Observable
18911  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18912  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18913  * <p>
18914  * 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
18915  * has no knowledge of the format of the data returned by the Proxy.<br>
18916  * <p>
18917  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18918  * instances from the data object. These records are cached and made available through accessor functions.
18919  * @constructor
18920  * Creates a new Store.
18921  * @param {Object} config A config object containing the objects needed for the Store to access data,
18922  * and read the data into Records.
18923  */
18924 Roo.data.Store = function(config){
18925     this.data = new Roo.util.MixedCollection(false);
18926     this.data.getKey = function(o){
18927         return o.id;
18928     };
18929     this.baseParams = {};
18930     // private
18931     this.paramNames = {
18932         "start" : "start",
18933         "limit" : "limit",
18934         "sort" : "sort",
18935         "dir" : "dir"
18936     };
18937
18938     if(config && config.data){
18939         this.inlineData = config.data;
18940         delete config.data;
18941     }
18942
18943     Roo.apply(this, config);
18944     
18945     if(this.reader){ // reader passed
18946         this.reader = Roo.factory(this.reader, Roo.data);
18947         this.reader.xmodule = this.xmodule || false;
18948         if(!this.recordType){
18949             this.recordType = this.reader.recordType;
18950         }
18951         if(this.reader.onMetaChange){
18952             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18953         }
18954     }
18955
18956     if(this.recordType){
18957         this.fields = this.recordType.prototype.fields;
18958     }
18959     this.modified = [];
18960
18961     this.addEvents({
18962         /**
18963          * @event datachanged
18964          * Fires when the data cache has changed, and a widget which is using this Store
18965          * as a Record cache should refresh its view.
18966          * @param {Store} this
18967          */
18968         datachanged : true,
18969         /**
18970          * @event metachange
18971          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18972          * @param {Store} this
18973          * @param {Object} meta The JSON metadata
18974          */
18975         metachange : true,
18976         /**
18977          * @event add
18978          * Fires when Records have been added to the Store
18979          * @param {Store} this
18980          * @param {Roo.data.Record[]} records The array of Records added
18981          * @param {Number} index The index at which the record(s) were added
18982          */
18983         add : true,
18984         /**
18985          * @event remove
18986          * Fires when a Record has been removed from the Store
18987          * @param {Store} this
18988          * @param {Roo.data.Record} record The Record that was removed
18989          * @param {Number} index The index at which the record was removed
18990          */
18991         remove : true,
18992         /**
18993          * @event update
18994          * Fires when a Record has been updated
18995          * @param {Store} this
18996          * @param {Roo.data.Record} record The Record that was updated
18997          * @param {String} operation The update operation being performed.  Value may be one of:
18998          * <pre><code>
18999  Roo.data.Record.EDIT
19000  Roo.data.Record.REJECT
19001  Roo.data.Record.COMMIT
19002          * </code></pre>
19003          */
19004         update : true,
19005         /**
19006          * @event clear
19007          * Fires when the data cache has been cleared.
19008          * @param {Store} this
19009          */
19010         clear : true,
19011         /**
19012          * @event beforeload
19013          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19014          * the load action will be canceled.
19015          * @param {Store} this
19016          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19017          */
19018         beforeload : true,
19019         /**
19020          * @event load
19021          * Fires after a new set of Records has been loaded.
19022          * @param {Store} this
19023          * @param {Roo.data.Record[]} records The Records that were loaded
19024          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19025          */
19026         load : true,
19027         /**
19028          * @event loadexception
19029          * Fires if an exception occurs in the Proxy during loading.
19030          * Called with the signature of the Proxy's "loadexception" event.
19031          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19032          * 
19033          * @param {Proxy} 
19034          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19035          * @param {Object} load options 
19036          * @param {Object} jsonData from your request (normally this contains the Exception)
19037          */
19038         loadexception : true
19039     });
19040     
19041     if(this.proxy){
19042         this.proxy = Roo.factory(this.proxy, Roo.data);
19043         this.proxy.xmodule = this.xmodule || false;
19044         this.relayEvents(this.proxy,  ["loadexception"]);
19045     }
19046     this.sortToggle = {};
19047
19048     Roo.data.Store.superclass.constructor.call(this);
19049
19050     if(this.inlineData){
19051         this.loadData(this.inlineData);
19052         delete this.inlineData;
19053     }
19054 };
19055 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19056      /**
19057     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19058     * without a remote query - used by combo/forms at present.
19059     */
19060     
19061     /**
19062     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19063     */
19064     /**
19065     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19066     */
19067     /**
19068     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19069     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19070     */
19071     /**
19072     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19073     * on any HTTP request
19074     */
19075     /**
19076     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19077     */
19078     /**
19079     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19080     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19081     */
19082     remoteSort : false,
19083
19084     /**
19085     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19086      * loaded or when a record is removed. (defaults to false).
19087     */
19088     pruneModifiedRecords : false,
19089
19090     // private
19091     lastOptions : null,
19092
19093     /**
19094      * Add Records to the Store and fires the add event.
19095      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19096      */
19097     add : function(records){
19098         records = [].concat(records);
19099         for(var i = 0, len = records.length; i < len; i++){
19100             records[i].join(this);
19101         }
19102         var index = this.data.length;
19103         this.data.addAll(records);
19104         this.fireEvent("add", this, records, index);
19105     },
19106
19107     /**
19108      * Remove a Record from the Store and fires the remove event.
19109      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19110      */
19111     remove : function(record){
19112         var index = this.data.indexOf(record);
19113         this.data.removeAt(index);
19114         if(this.pruneModifiedRecords){
19115             this.modified.remove(record);
19116         }
19117         this.fireEvent("remove", this, record, index);
19118     },
19119
19120     /**
19121      * Remove all Records from the Store and fires the clear event.
19122      */
19123     removeAll : function(){
19124         this.data.clear();
19125         if(this.pruneModifiedRecords){
19126             this.modified = [];
19127         }
19128         this.fireEvent("clear", this);
19129     },
19130
19131     /**
19132      * Inserts Records to the Store at the given index and fires the add event.
19133      * @param {Number} index The start index at which to insert the passed Records.
19134      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19135      */
19136     insert : function(index, records){
19137         records = [].concat(records);
19138         for(var i = 0, len = records.length; i < len; i++){
19139             this.data.insert(index, records[i]);
19140             records[i].join(this);
19141         }
19142         this.fireEvent("add", this, records, index);
19143     },
19144
19145     /**
19146      * Get the index within the cache of the passed Record.
19147      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19148      * @return {Number} The index of the passed Record. Returns -1 if not found.
19149      */
19150     indexOf : function(record){
19151         return this.data.indexOf(record);
19152     },
19153
19154     /**
19155      * Get the index within the cache of the Record with the passed id.
19156      * @param {String} id The id of the Record to find.
19157      * @return {Number} The index of the Record. Returns -1 if not found.
19158      */
19159     indexOfId : function(id){
19160         return this.data.indexOfKey(id);
19161     },
19162
19163     /**
19164      * Get the Record with the specified id.
19165      * @param {String} id The id of the Record to find.
19166      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19167      */
19168     getById : function(id){
19169         return this.data.key(id);
19170     },
19171
19172     /**
19173      * Get the Record at the specified index.
19174      * @param {Number} index The index of the Record to find.
19175      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19176      */
19177     getAt : function(index){
19178         return this.data.itemAt(index);
19179     },
19180
19181     /**
19182      * Returns a range of Records between specified indices.
19183      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19184      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19185      * @return {Roo.data.Record[]} An array of Records
19186      */
19187     getRange : function(start, end){
19188         return this.data.getRange(start, end);
19189     },
19190
19191     // private
19192     storeOptions : function(o){
19193         o = Roo.apply({}, o);
19194         delete o.callback;
19195         delete o.scope;
19196         this.lastOptions = o;
19197     },
19198
19199     /**
19200      * Loads the Record cache from the configured Proxy using the configured Reader.
19201      * <p>
19202      * If using remote paging, then the first load call must specify the <em>start</em>
19203      * and <em>limit</em> properties in the options.params property to establish the initial
19204      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19205      * <p>
19206      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19207      * and this call will return before the new data has been loaded. Perform any post-processing
19208      * in a callback function, or in a "load" event handler.</strong>
19209      * <p>
19210      * @param {Object} options An object containing properties which control loading options:<ul>
19211      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19212      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19213      * passed the following arguments:<ul>
19214      * <li>r : Roo.data.Record[]</li>
19215      * <li>options: Options object from the load call</li>
19216      * <li>success: Boolean success indicator</li></ul></li>
19217      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19218      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19219      * </ul>
19220      */
19221     load : function(options){
19222         options = options || {};
19223         if(this.fireEvent("beforeload", this, options) !== false){
19224             this.storeOptions(options);
19225             var p = Roo.apply(options.params || {}, this.baseParams);
19226             if(this.sortInfo && this.remoteSort){
19227                 var pn = this.paramNames;
19228                 p[pn["sort"]] = this.sortInfo.field;
19229                 p[pn["dir"]] = this.sortInfo.direction;
19230             }
19231             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19232         }
19233     },
19234
19235     /**
19236      * Reloads the Record cache from the configured Proxy using the configured Reader and
19237      * the options from the last load operation performed.
19238      * @param {Object} options (optional) An object containing properties which may override the options
19239      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19240      * the most recently used options are reused).
19241      */
19242     reload : function(options){
19243         this.load(Roo.applyIf(options||{}, this.lastOptions));
19244     },
19245
19246     // private
19247     // Called as a callback by the Reader during a load operation.
19248     loadRecords : function(o, options, success){
19249         if(!o || success === false){
19250             if(success !== false){
19251                 this.fireEvent("load", this, [], options);
19252             }
19253             if(options.callback){
19254                 options.callback.call(options.scope || this, [], options, false);
19255             }
19256             return;
19257         }
19258         // if data returned failure - throw an exception.
19259         if (o.success === false) {
19260             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19261             return;
19262         }
19263         var r = o.records, t = o.totalRecords || r.length;
19264         if(!options || options.add !== true){
19265             if(this.pruneModifiedRecords){
19266                 this.modified = [];
19267             }
19268             for(var i = 0, len = r.length; i < len; i++){
19269                 r[i].join(this);
19270             }
19271             if(this.snapshot){
19272                 this.data = this.snapshot;
19273                 delete this.snapshot;
19274             }
19275             this.data.clear();
19276             this.data.addAll(r);
19277             this.totalLength = t;
19278             this.applySort();
19279             this.fireEvent("datachanged", this);
19280         }else{
19281             this.totalLength = Math.max(t, this.data.length+r.length);
19282             this.add(r);
19283         }
19284         this.fireEvent("load", this, r, options);
19285         if(options.callback){
19286             options.callback.call(options.scope || this, r, options, true);
19287         }
19288     },
19289
19290     /**
19291      * Loads data from a passed data block. A Reader which understands the format of the data
19292      * must have been configured in the constructor.
19293      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19294      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19295      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19296      */
19297     loadData : function(o, append){
19298         var r = this.reader.readRecords(o);
19299         this.loadRecords(r, {add: append}, true);
19300     },
19301
19302     /**
19303      * Gets the number of cached records.
19304      * <p>
19305      * <em>If using paging, this may not be the total size of the dataset. If the data object
19306      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19307      * the data set size</em>
19308      */
19309     getCount : function(){
19310         return this.data.length || 0;
19311     },
19312
19313     /**
19314      * Gets the total number of records in the dataset as returned by the server.
19315      * <p>
19316      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19317      * the dataset size</em>
19318      */
19319     getTotalCount : function(){
19320         return this.totalLength || 0;
19321     },
19322
19323     /**
19324      * Returns the sort state of the Store as an object with two properties:
19325      * <pre><code>
19326  field {String} The name of the field by which the Records are sorted
19327  direction {String} The sort order, "ASC" or "DESC"
19328      * </code></pre>
19329      */
19330     getSortState : function(){
19331         return this.sortInfo;
19332     },
19333
19334     // private
19335     applySort : function(){
19336         if(this.sortInfo && !this.remoteSort){
19337             var s = this.sortInfo, f = s.field;
19338             var st = this.fields.get(f).sortType;
19339             var fn = function(r1, r2){
19340                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19341                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19342             };
19343             this.data.sort(s.direction, fn);
19344             if(this.snapshot && this.snapshot != this.data){
19345                 this.snapshot.sort(s.direction, fn);
19346             }
19347         }
19348     },
19349
19350     /**
19351      * Sets the default sort column and order to be used by the next load operation.
19352      * @param {String} fieldName The name of the field to sort by.
19353      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19354      */
19355     setDefaultSort : function(field, dir){
19356         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19357     },
19358
19359     /**
19360      * Sort the Records.
19361      * If remote sorting is used, the sort is performed on the server, and the cache is
19362      * reloaded. If local sorting is used, the cache is sorted internally.
19363      * @param {String} fieldName The name of the field to sort by.
19364      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19365      */
19366     sort : function(fieldName, dir){
19367         var f = this.fields.get(fieldName);
19368         if(!dir){
19369             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19370                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19371             }else{
19372                 dir = f.sortDir;
19373             }
19374         }
19375         this.sortToggle[f.name] = dir;
19376         this.sortInfo = {field: f.name, direction: dir};
19377         if(!this.remoteSort){
19378             this.applySort();
19379             this.fireEvent("datachanged", this);
19380         }else{
19381             this.load(this.lastOptions);
19382         }
19383     },
19384
19385     /**
19386      * Calls the specified function for each of the Records in the cache.
19387      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19388      * Returning <em>false</em> aborts and exits the iteration.
19389      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19390      */
19391     each : function(fn, scope){
19392         this.data.each(fn, scope);
19393     },
19394
19395     /**
19396      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19397      * (e.g., during paging).
19398      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19399      */
19400     getModifiedRecords : function(){
19401         return this.modified;
19402     },
19403
19404     // private
19405     createFilterFn : function(property, value, anyMatch){
19406         if(!value.exec){ // not a regex
19407             value = String(value);
19408             if(value.length == 0){
19409                 return false;
19410             }
19411             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19412         }
19413         return function(r){
19414             return value.test(r.data[property]);
19415         };
19416     },
19417
19418     /**
19419      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19420      * @param {String} property A field on your records
19421      * @param {Number} start The record index to start at (defaults to 0)
19422      * @param {Number} end The last record index to include (defaults to length - 1)
19423      * @return {Number} The sum
19424      */
19425     sum : function(property, start, end){
19426         var rs = this.data.items, v = 0;
19427         start = start || 0;
19428         end = (end || end === 0) ? end : rs.length-1;
19429
19430         for(var i = start; i <= end; i++){
19431             v += (rs[i].data[property] || 0);
19432         }
19433         return v;
19434     },
19435
19436     /**
19437      * Filter the records by a specified property.
19438      * @param {String} field A field on your records
19439      * @param {String/RegExp} value Either a string that the field
19440      * should start with or a RegExp to test against the field
19441      * @param {Boolean} anyMatch True to match any part not just the beginning
19442      */
19443     filter : function(property, value, anyMatch){
19444         var fn = this.createFilterFn(property, value, anyMatch);
19445         return fn ? this.filterBy(fn) : this.clearFilter();
19446     },
19447
19448     /**
19449      * Filter by a function. The specified function will be called with each
19450      * record in this data source. If the function returns true the record is included,
19451      * otherwise it is filtered.
19452      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19453      * @param {Object} scope (optional) The scope of the function (defaults to this)
19454      */
19455     filterBy : function(fn, scope){
19456         this.snapshot = this.snapshot || this.data;
19457         this.data = this.queryBy(fn, scope||this);
19458         this.fireEvent("datachanged", this);
19459     },
19460
19461     /**
19462      * Query the records by a specified property.
19463      * @param {String} field A field on your records
19464      * @param {String/RegExp} value Either a string that the field
19465      * should start with or a RegExp to test against the field
19466      * @param {Boolean} anyMatch True to match any part not just the beginning
19467      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19468      */
19469     query : function(property, value, anyMatch){
19470         var fn = this.createFilterFn(property, value, anyMatch);
19471         return fn ? this.queryBy(fn) : this.data.clone();
19472     },
19473
19474     /**
19475      * Query by a function. The specified function will be called with each
19476      * record in this data source. If the function returns true the record is included
19477      * in the results.
19478      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19479      * @param {Object} scope (optional) The scope of the function (defaults to this)
19480       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19481      **/
19482     queryBy : function(fn, scope){
19483         var data = this.snapshot || this.data;
19484         return data.filterBy(fn, scope||this);
19485     },
19486
19487     /**
19488      * Collects unique values for a particular dataIndex from this store.
19489      * @param {String} dataIndex The property to collect
19490      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19491      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19492      * @return {Array} An array of the unique values
19493      **/
19494     collect : function(dataIndex, allowNull, bypassFilter){
19495         var d = (bypassFilter === true && this.snapshot) ?
19496                 this.snapshot.items : this.data.items;
19497         var v, sv, r = [], l = {};
19498         for(var i = 0, len = d.length; i < len; i++){
19499             v = d[i].data[dataIndex];
19500             sv = String(v);
19501             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19502                 l[sv] = true;
19503                 r[r.length] = v;
19504             }
19505         }
19506         return r;
19507     },
19508
19509     /**
19510      * Revert to a view of the Record cache with no filtering applied.
19511      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19512      */
19513     clearFilter : function(suppressEvent){
19514         if(this.snapshot && this.snapshot != this.data){
19515             this.data = this.snapshot;
19516             delete this.snapshot;
19517             if(suppressEvent !== true){
19518                 this.fireEvent("datachanged", this);
19519             }
19520         }
19521     },
19522
19523     // private
19524     afterEdit : function(record){
19525         if(this.modified.indexOf(record) == -1){
19526             this.modified.push(record);
19527         }
19528         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19529     },
19530
19531     // private
19532     afterReject : function(record){
19533         this.modified.remove(record);
19534         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19535     },
19536
19537     // private
19538     afterCommit : function(record){
19539         this.modified.remove(record);
19540         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19541     },
19542
19543     /**
19544      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19545      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19546      */
19547     commitChanges : function(){
19548         var m = this.modified.slice(0);
19549         this.modified = [];
19550         for(var i = 0, len = m.length; i < len; i++){
19551             m[i].commit();
19552         }
19553     },
19554
19555     /**
19556      * Cancel outstanding changes on all changed records.
19557      */
19558     rejectChanges : function(){
19559         var m = this.modified.slice(0);
19560         this.modified = [];
19561         for(var i = 0, len = m.length; i < len; i++){
19562             m[i].reject();
19563         }
19564     },
19565
19566     onMetaChange : function(meta, rtype, o){
19567         this.recordType = rtype;
19568         this.fields = rtype.prototype.fields;
19569         delete this.snapshot;
19570         this.sortInfo = meta.sortInfo;
19571         this.modified = [];
19572         this.fireEvent('metachange', this, this.reader.meta);
19573     }
19574 });/*
19575  * Based on:
19576  * Ext JS Library 1.1.1
19577  * Copyright(c) 2006-2007, Ext JS, LLC.
19578  *
19579  * Originally Released Under LGPL - original licence link has changed is not relivant.
19580  *
19581  * Fork - LGPL
19582  * <script type="text/javascript">
19583  */
19584
19585 /**
19586  * @class Roo.data.SimpleStore
19587  * @extends Roo.data.Store
19588  * Small helper class to make creating Stores from Array data easier.
19589  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19590  * @cfg {Array} fields An array of field definition objects, or field name strings.
19591  * @cfg {Array} data The multi-dimensional array of data
19592  * @constructor
19593  * @param {Object} config
19594  */
19595 Roo.data.SimpleStore = function(config){
19596     Roo.data.SimpleStore.superclass.constructor.call(this, {
19597         isLocal : true,
19598         reader: new Roo.data.ArrayReader({
19599                 id: config.id
19600             },
19601             Roo.data.Record.create(config.fields)
19602         ),
19603         proxy : new Roo.data.MemoryProxy(config.data)
19604     });
19605     this.load();
19606 };
19607 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19608  * Based on:
19609  * Ext JS Library 1.1.1
19610  * Copyright(c) 2006-2007, Ext JS, LLC.
19611  *
19612  * Originally Released Under LGPL - original licence link has changed is not relivant.
19613  *
19614  * Fork - LGPL
19615  * <script type="text/javascript">
19616  */
19617
19618 /**
19619 /**
19620  * @extends Roo.data.Store
19621  * @class Roo.data.JsonStore
19622  * Small helper class to make creating Stores for JSON data easier. <br/>
19623 <pre><code>
19624 var store = new Roo.data.JsonStore({
19625     url: 'get-images.php',
19626     root: 'images',
19627     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19628 });
19629 </code></pre>
19630  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19631  * JsonReader and HttpProxy (unless inline data is provided).</b>
19632  * @cfg {Array} fields An array of field definition objects, or field name strings.
19633  * @constructor
19634  * @param {Object} config
19635  */
19636 Roo.data.JsonStore = function(c){
19637     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19638         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19639         reader: new Roo.data.JsonReader(c, c.fields)
19640     }));
19641 };
19642 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19643  * Based on:
19644  * Ext JS Library 1.1.1
19645  * Copyright(c) 2006-2007, Ext JS, LLC.
19646  *
19647  * Originally Released Under LGPL - original licence link has changed is not relivant.
19648  *
19649  * Fork - LGPL
19650  * <script type="text/javascript">
19651  */
19652
19653  
19654 Roo.data.Field = function(config){
19655     if(typeof config == "string"){
19656         config = {name: config};
19657     }
19658     Roo.apply(this, config);
19659     
19660     if(!this.type){
19661         this.type = "auto";
19662     }
19663     
19664     var st = Roo.data.SortTypes;
19665     // named sortTypes are supported, here we look them up
19666     if(typeof this.sortType == "string"){
19667         this.sortType = st[this.sortType];
19668     }
19669     
19670     // set default sortType for strings and dates
19671     if(!this.sortType){
19672         switch(this.type){
19673             case "string":
19674                 this.sortType = st.asUCString;
19675                 break;
19676             case "date":
19677                 this.sortType = st.asDate;
19678                 break;
19679             default:
19680                 this.sortType = st.none;
19681         }
19682     }
19683
19684     // define once
19685     var stripRe = /[\$,%]/g;
19686
19687     // prebuilt conversion function for this field, instead of
19688     // switching every time we're reading a value
19689     if(!this.convert){
19690         var cv, dateFormat = this.dateFormat;
19691         switch(this.type){
19692             case "":
19693             case "auto":
19694             case undefined:
19695                 cv = function(v){ return v; };
19696                 break;
19697             case "string":
19698                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19699                 break;
19700             case "int":
19701                 cv = function(v){
19702                     return v !== undefined && v !== null && v !== '' ?
19703                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19704                     };
19705                 break;
19706             case "float":
19707                 cv = function(v){
19708                     return v !== undefined && v !== null && v !== '' ?
19709                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19710                     };
19711                 break;
19712             case "bool":
19713             case "boolean":
19714                 cv = function(v){ return v === true || v === "true" || v == 1; };
19715                 break;
19716             case "date":
19717                 cv = function(v){
19718                     if(!v){
19719                         return '';
19720                     }
19721                     if(v instanceof Date){
19722                         return v;
19723                     }
19724                     if(dateFormat){
19725                         if(dateFormat == "timestamp"){
19726                             return new Date(v*1000);
19727                         }
19728                         return Date.parseDate(v, dateFormat);
19729                     }
19730                     var parsed = Date.parse(v);
19731                     return parsed ? new Date(parsed) : null;
19732                 };
19733              break;
19734             
19735         }
19736         this.convert = cv;
19737     }
19738 };
19739
19740 Roo.data.Field.prototype = {
19741     dateFormat: null,
19742     defaultValue: "",
19743     mapping: null,
19744     sortType : null,
19745     sortDir : "ASC"
19746 };/*
19747  * Based on:
19748  * Ext JS Library 1.1.1
19749  * Copyright(c) 2006-2007, Ext JS, LLC.
19750  *
19751  * Originally Released Under LGPL - original licence link has changed is not relivant.
19752  *
19753  * Fork - LGPL
19754  * <script type="text/javascript">
19755  */
19756  
19757 // Base class for reading structured data from a data source.  This class is intended to be
19758 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19759
19760 /**
19761  * @class Roo.data.DataReader
19762  * Base class for reading structured data from a data source.  This class is intended to be
19763  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19764  */
19765
19766 Roo.data.DataReader = function(meta, recordType){
19767     
19768     this.meta = meta;
19769     
19770     this.recordType = recordType instanceof Array ? 
19771         Roo.data.Record.create(recordType) : recordType;
19772 };
19773
19774 Roo.data.DataReader.prototype = {
19775      /**
19776      * Create an empty record
19777      * @param {Object} data (optional) - overlay some values
19778      * @return {Roo.data.Record} record created.
19779      */
19780     newRow :  function(d) {
19781         var da =  {};
19782         this.recordType.prototype.fields.each(function(c) {
19783             switch( c.type) {
19784                 case 'int' : da[c.name] = 0; break;
19785                 case 'date' : da[c.name] = new Date(); break;
19786                 case 'float' : da[c.name] = 0.0; break;
19787                 case 'boolean' : da[c.name] = false; break;
19788                 default : da[c.name] = ""; break;
19789             }
19790             
19791         });
19792         return new this.recordType(Roo.apply(da, d));
19793     }
19794     
19795 };/*
19796  * Based on:
19797  * Ext JS Library 1.1.1
19798  * Copyright(c) 2006-2007, Ext JS, LLC.
19799  *
19800  * Originally Released Under LGPL - original licence link has changed is not relivant.
19801  *
19802  * Fork - LGPL
19803  * <script type="text/javascript">
19804  */
19805
19806 /**
19807  * @class Roo.data.DataProxy
19808  * @extends Roo.data.Observable
19809  * This class is an abstract base class for implementations which provide retrieval of
19810  * unformatted data objects.<br>
19811  * <p>
19812  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19813  * (of the appropriate type which knows how to parse the data object) to provide a block of
19814  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19815  * <p>
19816  * Custom implementations must implement the load method as described in
19817  * {@link Roo.data.HttpProxy#load}.
19818  */
19819 Roo.data.DataProxy = function(){
19820     this.addEvents({
19821         /**
19822          * @event beforeload
19823          * Fires before a network request is made to retrieve a data object.
19824          * @param {Object} This DataProxy object.
19825          * @param {Object} params The params parameter to the load function.
19826          */
19827         beforeload : true,
19828         /**
19829          * @event load
19830          * Fires before the load method's callback is called.
19831          * @param {Object} This DataProxy object.
19832          * @param {Object} o The data object.
19833          * @param {Object} arg The callback argument object passed to the load function.
19834          */
19835         load : true,
19836         /**
19837          * @event loadexception
19838          * Fires if an Exception occurs during data retrieval.
19839          * @param {Object} This DataProxy object.
19840          * @param {Object} o The data object.
19841          * @param {Object} arg The callback argument object passed to the load function.
19842          * @param {Object} e The Exception.
19843          */
19844         loadexception : true
19845     });
19846     Roo.data.DataProxy.superclass.constructor.call(this);
19847 };
19848
19849 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19850
19851     /**
19852      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19853      */
19854 /*
19855  * Based on:
19856  * Ext JS Library 1.1.1
19857  * Copyright(c) 2006-2007, Ext JS, LLC.
19858  *
19859  * Originally Released Under LGPL - original licence link has changed is not relivant.
19860  *
19861  * Fork - LGPL
19862  * <script type="text/javascript">
19863  */
19864 /**
19865  * @class Roo.data.MemoryProxy
19866  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19867  * to the Reader when its load method is called.
19868  * @constructor
19869  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19870  */
19871 Roo.data.MemoryProxy = function(data){
19872     if (data.data) {
19873         data = data.data;
19874     }
19875     Roo.data.MemoryProxy.superclass.constructor.call(this);
19876     this.data = data;
19877 };
19878
19879 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19880     /**
19881      * Load data from the requested source (in this case an in-memory
19882      * data object passed to the constructor), read the data object into
19883      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19884      * process that block using the passed callback.
19885      * @param {Object} params This parameter is not used by the MemoryProxy class.
19886      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19887      * object into a block of Roo.data.Records.
19888      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19889      * The function must be passed <ul>
19890      * <li>The Record block object</li>
19891      * <li>The "arg" argument from the load function</li>
19892      * <li>A boolean success indicator</li>
19893      * </ul>
19894      * @param {Object} scope The scope in which to call the callback
19895      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19896      */
19897     load : function(params, reader, callback, scope, arg){
19898         params = params || {};
19899         var result;
19900         try {
19901             result = reader.readRecords(this.data);
19902         }catch(e){
19903             this.fireEvent("loadexception", this, arg, null, e);
19904             callback.call(scope, null, arg, false);
19905             return;
19906         }
19907         callback.call(scope, result, arg, true);
19908     },
19909     
19910     // private
19911     update : function(params, records){
19912         
19913     }
19914 });/*
19915  * Based on:
19916  * Ext JS Library 1.1.1
19917  * Copyright(c) 2006-2007, Ext JS, LLC.
19918  *
19919  * Originally Released Under LGPL - original licence link has changed is not relivant.
19920  *
19921  * Fork - LGPL
19922  * <script type="text/javascript">
19923  */
19924 /**
19925  * @class Roo.data.HttpProxy
19926  * @extends Roo.data.DataProxy
19927  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19928  * configured to reference a certain URL.<br><br>
19929  * <p>
19930  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19931  * from which the running page was served.<br><br>
19932  * <p>
19933  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19934  * <p>
19935  * Be aware that to enable the browser to parse an XML document, the server must set
19936  * the Content-Type header in the HTTP response to "text/xml".
19937  * @constructor
19938  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19939  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19940  * will be used to make the request.
19941  */
19942 Roo.data.HttpProxy = function(conn){
19943     Roo.data.HttpProxy.superclass.constructor.call(this);
19944     // is conn a conn config or a real conn?
19945     this.conn = conn;
19946     this.useAjax = !conn || !conn.events;
19947   
19948 };
19949
19950 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19951     // thse are take from connection...
19952     
19953     /**
19954      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19955      */
19956     /**
19957      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19958      * extra parameters to each request made by this object. (defaults to undefined)
19959      */
19960     /**
19961      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19962      *  to each request made by this object. (defaults to undefined)
19963      */
19964     /**
19965      * @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)
19966      */
19967     /**
19968      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19969      */
19970      /**
19971      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19972      * @type Boolean
19973      */
19974   
19975
19976     /**
19977      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19978      * @type Boolean
19979      */
19980     /**
19981      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19982      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19983      * a finer-grained basis than the DataProxy events.
19984      */
19985     getConnection : function(){
19986         return this.useAjax ? Roo.Ajax : this.conn;
19987     },
19988
19989     /**
19990      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19991      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19992      * process that block using the passed callback.
19993      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19994      * for the request to the remote server.
19995      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19996      * object into a block of Roo.data.Records.
19997      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19998      * The function must be passed <ul>
19999      * <li>The Record block object</li>
20000      * <li>The "arg" argument from the load function</li>
20001      * <li>A boolean success indicator</li>
20002      * </ul>
20003      * @param {Object} scope The scope in which to call the callback
20004      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20005      */
20006     load : function(params, reader, callback, scope, arg){
20007         if(this.fireEvent("beforeload", this, params) !== false){
20008             var  o = {
20009                 params : params || {},
20010                 request: {
20011                     callback : callback,
20012                     scope : scope,
20013                     arg : arg
20014                 },
20015                 reader: reader,
20016                 callback : this.loadResponse,
20017                 scope: this
20018             };
20019             if(this.useAjax){
20020                 Roo.applyIf(o, this.conn);
20021                 if(this.activeRequest){
20022                     Roo.Ajax.abort(this.activeRequest);
20023                 }
20024                 this.activeRequest = Roo.Ajax.request(o);
20025             }else{
20026                 this.conn.request(o);
20027             }
20028         }else{
20029             callback.call(scope||this, null, arg, false);
20030         }
20031     },
20032
20033     // private
20034     loadResponse : function(o, success, response){
20035         delete this.activeRequest;
20036         if(!success){
20037             this.fireEvent("loadexception", this, o, response);
20038             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20039             return;
20040         }
20041         var result;
20042         try {
20043             result = o.reader.read(response);
20044         }catch(e){
20045             this.fireEvent("loadexception", this, o, response, e);
20046             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20047             return;
20048         }
20049         
20050         this.fireEvent("load", this, o, o.request.arg);
20051         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20052     },
20053
20054     // private
20055     update : function(dataSet){
20056
20057     },
20058
20059     // private
20060     updateResponse : function(dataSet){
20061
20062     }
20063 });/*
20064  * Based on:
20065  * Ext JS Library 1.1.1
20066  * Copyright(c) 2006-2007, Ext JS, LLC.
20067  *
20068  * Originally Released Under LGPL - original licence link has changed is not relivant.
20069  *
20070  * Fork - LGPL
20071  * <script type="text/javascript">
20072  */
20073
20074 /**
20075  * @class Roo.data.ScriptTagProxy
20076  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20077  * other than the originating domain of the running page.<br><br>
20078  * <p>
20079  * <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
20080  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20081  * <p>
20082  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20083  * source code that is used as the source inside a &lt;script> tag.<br><br>
20084  * <p>
20085  * In order for the browser to process the returned data, the server must wrap the data object
20086  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20087  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20088  * depending on whether the callback name was passed:
20089  * <p>
20090  * <pre><code>
20091 boolean scriptTag = false;
20092 String cb = request.getParameter("callback");
20093 if (cb != null) {
20094     scriptTag = true;
20095     response.setContentType("text/javascript");
20096 } else {
20097     response.setContentType("application/x-json");
20098 }
20099 Writer out = response.getWriter();
20100 if (scriptTag) {
20101     out.write(cb + "(");
20102 }
20103 out.print(dataBlock.toJsonString());
20104 if (scriptTag) {
20105     out.write(");");
20106 }
20107 </pre></code>
20108  *
20109  * @constructor
20110  * @param {Object} config A configuration object.
20111  */
20112 Roo.data.ScriptTagProxy = function(config){
20113     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20114     Roo.apply(this, config);
20115     this.head = document.getElementsByTagName("head")[0];
20116 };
20117
20118 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20119
20120 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20121     /**
20122      * @cfg {String} url The URL from which to request the data object.
20123      */
20124     /**
20125      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20126      */
20127     timeout : 30000,
20128     /**
20129      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20130      * the server the name of the callback function set up by the load call to process the returned data object.
20131      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20132      * javascript output which calls this named function passing the data object as its only parameter.
20133      */
20134     callbackParam : "callback",
20135     /**
20136      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20137      * name to the request.
20138      */
20139     nocache : true,
20140
20141     /**
20142      * Load data from the configured URL, read the data object into
20143      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20144      * process that block using the passed callback.
20145      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20146      * for the request to the remote server.
20147      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20148      * object into a block of Roo.data.Records.
20149      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20150      * The function must be passed <ul>
20151      * <li>The Record block object</li>
20152      * <li>The "arg" argument from the load function</li>
20153      * <li>A boolean success indicator</li>
20154      * </ul>
20155      * @param {Object} scope The scope in which to call the callback
20156      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20157      */
20158     load : function(params, reader, callback, scope, arg){
20159         if(this.fireEvent("beforeload", this, params) !== false){
20160
20161             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20162
20163             var url = this.url;
20164             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20165             if(this.nocache){
20166                 url += "&_dc=" + (new Date().getTime());
20167             }
20168             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20169             var trans = {
20170                 id : transId,
20171                 cb : "stcCallback"+transId,
20172                 scriptId : "stcScript"+transId,
20173                 params : params,
20174                 arg : arg,
20175                 url : url,
20176                 callback : callback,
20177                 scope : scope,
20178                 reader : reader
20179             };
20180             var conn = this;
20181
20182             window[trans.cb] = function(o){
20183                 conn.handleResponse(o, trans);
20184             };
20185
20186             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20187
20188             if(this.autoAbort !== false){
20189                 this.abort();
20190             }
20191
20192             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20193
20194             var script = document.createElement("script");
20195             script.setAttribute("src", url);
20196             script.setAttribute("type", "text/javascript");
20197             script.setAttribute("id", trans.scriptId);
20198             this.head.appendChild(script);
20199
20200             this.trans = trans;
20201         }else{
20202             callback.call(scope||this, null, arg, false);
20203         }
20204     },
20205
20206     // private
20207     isLoading : function(){
20208         return this.trans ? true : false;
20209     },
20210
20211     /**
20212      * Abort the current server request.
20213      */
20214     abort : function(){
20215         if(this.isLoading()){
20216             this.destroyTrans(this.trans);
20217         }
20218     },
20219
20220     // private
20221     destroyTrans : function(trans, isLoaded){
20222         this.head.removeChild(document.getElementById(trans.scriptId));
20223         clearTimeout(trans.timeoutId);
20224         if(isLoaded){
20225             window[trans.cb] = undefined;
20226             try{
20227                 delete window[trans.cb];
20228             }catch(e){}
20229         }else{
20230             // if hasn't been loaded, wait for load to remove it to prevent script error
20231             window[trans.cb] = function(){
20232                 window[trans.cb] = undefined;
20233                 try{
20234                     delete window[trans.cb];
20235                 }catch(e){}
20236             };
20237         }
20238     },
20239
20240     // private
20241     handleResponse : function(o, trans){
20242         this.trans = false;
20243         this.destroyTrans(trans, true);
20244         var result;
20245         try {
20246             result = trans.reader.readRecords(o);
20247         }catch(e){
20248             this.fireEvent("loadexception", this, o, trans.arg, e);
20249             trans.callback.call(trans.scope||window, null, trans.arg, false);
20250             return;
20251         }
20252         this.fireEvent("load", this, o, trans.arg);
20253         trans.callback.call(trans.scope||window, result, trans.arg, true);
20254     },
20255
20256     // private
20257     handleFailure : function(trans){
20258         this.trans = false;
20259         this.destroyTrans(trans, false);
20260         this.fireEvent("loadexception", this, null, trans.arg);
20261         trans.callback.call(trans.scope||window, null, trans.arg, false);
20262     }
20263 });/*
20264  * Based on:
20265  * Ext JS Library 1.1.1
20266  * Copyright(c) 2006-2007, Ext JS, LLC.
20267  *
20268  * Originally Released Under LGPL - original licence link has changed is not relivant.
20269  *
20270  * Fork - LGPL
20271  * <script type="text/javascript">
20272  */
20273
20274 /**
20275  * @class Roo.data.JsonReader
20276  * @extends Roo.data.DataReader
20277  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20278  * based on mappings in a provided Roo.data.Record constructor.
20279  * <p>
20280  * Example code:
20281  * <pre><code>
20282 var RecordDef = Roo.data.Record.create([
20283     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20284     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20285 ]);
20286 var myReader = new Roo.data.JsonReader({
20287     totalProperty: "results",    // The property which contains the total dataset size (optional)
20288     root: "rows",                // The property which contains an Array of row objects
20289     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20290 }, RecordDef);
20291 </code></pre>
20292  * <p>
20293  * This would consume a JSON file like this:
20294  * <pre><code>
20295 { 'results': 2, 'rows': [
20296     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20297     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20298 }
20299 </code></pre>
20300  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20301  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20302  * paged from the remote server.
20303  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20304  * @cfg {String} root name of the property which contains the Array of row objects.
20305  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20306  * @constructor
20307  * Create a new JsonReader
20308  * @param {Object} meta Metadata configuration options
20309  * @param {Object} recordType Either an Array of field definition objects,
20310  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20311  */
20312 Roo.data.JsonReader = function(meta, recordType){
20313     
20314     meta = meta || {};
20315     // set some defaults:
20316     Roo.applyIf(meta, {
20317         totalProperty: 'total',
20318         successProperty : 'success',
20319         root : 'data',
20320         id : 'id'
20321     });
20322     
20323     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20324 };
20325 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20326     /**
20327      * This method is only used by a DataProxy which has retrieved data from a remote server.
20328      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20329      * @return {Object} data A data block which is used by an Roo.data.Store object as
20330      * a cache of Roo.data.Records.
20331      */
20332     read : function(response){
20333         var json = response.responseText;
20334         /* eval:var:o */
20335         var o = eval("("+json+")");
20336         if(!o) {
20337             throw {message: "JsonReader.read: Json object not found"};
20338         }
20339         
20340         if(o.metaData){
20341             delete this.ef;
20342             this.meta = o.metaData;
20343             this.recordType = Roo.data.Record.create(o.metaData.fields);
20344             this.onMetaChange(this.meta, this.recordType, o);
20345         }
20346         return this.readRecords(o);
20347     },
20348
20349     // private function a store will implement
20350     onMetaChange : function(meta, recordType, o){
20351
20352     },
20353
20354     /**
20355          * @ignore
20356          */
20357     simpleAccess: function(obj, subsc) {
20358         return obj[subsc];
20359     },
20360
20361         /**
20362          * @ignore
20363          */
20364     getJsonAccessor: function(){
20365         var re = /[\[\.]/;
20366         return function(expr) {
20367             try {
20368                 return(re.test(expr))
20369                     ? new Function("obj", "return obj." + expr)
20370                     : function(obj){
20371                         return obj[expr];
20372                     };
20373             } catch(e){}
20374             return Roo.emptyFn;
20375         };
20376     }(),
20377
20378     /**
20379      * Create a data block containing Roo.data.Records from an XML document.
20380      * @param {Object} o An object which contains an Array of row objects in the property specified
20381      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20382      * which contains the total size of the dataset.
20383      * @return {Object} data A data block which is used by an Roo.data.Store object as
20384      * a cache of Roo.data.Records.
20385      */
20386     readRecords : function(o){
20387         /**
20388          * After any data loads, the raw JSON data is available for further custom processing.
20389          * @type Object
20390          */
20391         this.jsonData = o;
20392         var s = this.meta, Record = this.recordType,
20393             f = Record.prototype.fields, fi = f.items, fl = f.length;
20394
20395 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20396         if (!this.ef) {
20397             if(s.totalProperty) {
20398                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20399                 }
20400                 if(s.successProperty) {
20401                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20402                 }
20403                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20404                 if (s.id) {
20405                         var g = this.getJsonAccessor(s.id);
20406                         this.getId = function(rec) {
20407                                 var r = g(rec);
20408                                 return (r === undefined || r === "") ? null : r;
20409                         };
20410                 } else {
20411                         this.getId = function(){return null;};
20412                 }
20413             this.ef = [];
20414             for(var i = 0; i < fl; i++){
20415                 f = fi[i];
20416                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20417                 this.ef[i] = this.getJsonAccessor(map);
20418             }
20419         }
20420
20421         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20422         if(s.totalProperty){
20423             var v = parseInt(this.getTotal(o), 10);
20424             if(!isNaN(v)){
20425                 totalRecords = v;
20426             }
20427         }
20428         if(s.successProperty){
20429             var v = this.getSuccess(o);
20430             if(v === false || v === 'false'){
20431                 success = false;
20432             }
20433         }
20434         var records = [];
20435             for(var i = 0; i < c; i++){
20436                     var n = root[i];
20437                 var values = {};
20438                 var id = this.getId(n);
20439                 for(var j = 0; j < fl; j++){
20440                     f = fi[j];
20441                 var v = this.ef[j](n);
20442                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20443                 }
20444                 var record = new Record(values, id);
20445                 record.json = n;
20446                 records[i] = record;
20447             }
20448             return {
20449                 success : success,
20450                 records : records,
20451                 totalRecords : totalRecords
20452             };
20453     }
20454 });/*
20455  * Based on:
20456  * Ext JS Library 1.1.1
20457  * Copyright(c) 2006-2007, Ext JS, LLC.
20458  *
20459  * Originally Released Under LGPL - original licence link has changed is not relivant.
20460  *
20461  * Fork - LGPL
20462  * <script type="text/javascript">
20463  */
20464
20465 /**
20466  * @class Roo.data.XmlReader
20467  * @extends Roo.data.DataReader
20468  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20469  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20470  * <p>
20471  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20472  * header in the HTTP response must be set to "text/xml".</em>
20473  * <p>
20474  * Example code:
20475  * <pre><code>
20476 var RecordDef = Roo.data.Record.create([
20477    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20478    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20479 ]);
20480 var myReader = new Roo.data.XmlReader({
20481    totalRecords: "results", // The element which contains the total dataset size (optional)
20482    record: "row",           // The repeated element which contains row information
20483    id: "id"                 // The element within the row that provides an ID for the record (optional)
20484 }, RecordDef);
20485 </code></pre>
20486  * <p>
20487  * This would consume an XML file like this:
20488  * <pre><code>
20489 &lt;?xml?>
20490 &lt;dataset>
20491  &lt;results>2&lt;/results>
20492  &lt;row>
20493    &lt;id>1&lt;/id>
20494    &lt;name>Bill&lt;/name>
20495    &lt;occupation>Gardener&lt;/occupation>
20496  &lt;/row>
20497  &lt;row>
20498    &lt;id>2&lt;/id>
20499    &lt;name>Ben&lt;/name>
20500    &lt;occupation>Horticulturalist&lt;/occupation>
20501  &lt;/row>
20502 &lt;/dataset>
20503 </code></pre>
20504  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20505  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20506  * paged from the remote server.
20507  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20508  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20509  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20510  * a record identifier value.
20511  * @constructor
20512  * Create a new XmlReader
20513  * @param {Object} meta Metadata configuration options
20514  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20515  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20516  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20517  */
20518 Roo.data.XmlReader = function(meta, recordType){
20519     meta = meta || {};
20520     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20521 };
20522 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20523     /**
20524      * This method is only used by a DataProxy which has retrieved data from a remote server.
20525          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20526          * to contain a method called 'responseXML' that returns an XML document object.
20527      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20528      * a cache of Roo.data.Records.
20529      */
20530     read : function(response){
20531         var doc = response.responseXML;
20532         if(!doc) {
20533             throw {message: "XmlReader.read: XML Document not available"};
20534         }
20535         return this.readRecords(doc);
20536     },
20537
20538     /**
20539      * Create a data block containing Roo.data.Records from an XML document.
20540          * @param {Object} doc A parsed XML document.
20541      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20542      * a cache of Roo.data.Records.
20543      */
20544     readRecords : function(doc){
20545         /**
20546          * After any data loads/reads, the raw XML Document is available for further custom processing.
20547          * @type XMLDocument
20548          */
20549         this.xmlData = doc;
20550         var root = doc.documentElement || doc;
20551         var q = Roo.DomQuery;
20552         var recordType = this.recordType, fields = recordType.prototype.fields;
20553         var sid = this.meta.id;
20554         var totalRecords = 0, success = true;
20555         if(this.meta.totalRecords){
20556             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20557         }
20558         
20559         if(this.meta.success){
20560             var sv = q.selectValue(this.meta.success, root, true);
20561             success = sv !== false && sv !== 'false';
20562         }
20563         var records = [];
20564         var ns = q.select(this.meta.record, root);
20565         for(var i = 0, len = ns.length; i < len; i++) {
20566                 var n = ns[i];
20567                 var values = {};
20568                 var id = sid ? q.selectValue(sid, n) : undefined;
20569                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20570                     var f = fields.items[j];
20571                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20572                     v = f.convert(v);
20573                     values[f.name] = v;
20574                 }
20575                 var record = new recordType(values, id);
20576                 record.node = n;
20577                 records[records.length] = record;
20578             }
20579
20580             return {
20581                 success : success,
20582                 records : records,
20583                 totalRecords : totalRecords || records.length
20584             };
20585     }
20586 });/*
20587  * Based on:
20588  * Ext JS Library 1.1.1
20589  * Copyright(c) 2006-2007, Ext JS, LLC.
20590  *
20591  * Originally Released Under LGPL - original licence link has changed is not relivant.
20592  *
20593  * Fork - LGPL
20594  * <script type="text/javascript">
20595  */
20596
20597 /**
20598  * @class Roo.data.ArrayReader
20599  * @extends Roo.data.DataReader
20600  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20601  * Each element of that Array represents a row of data fields. The
20602  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20603  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20604  * <p>
20605  * Example code:.
20606  * <pre><code>
20607 var RecordDef = Roo.data.Record.create([
20608     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20609     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20610 ]);
20611 var myReader = new Roo.data.ArrayReader({
20612     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20613 }, RecordDef);
20614 </code></pre>
20615  * <p>
20616  * This would consume an Array like this:
20617  * <pre><code>
20618 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20619   </code></pre>
20620  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20621  * @constructor
20622  * Create a new JsonReader
20623  * @param {Object} meta Metadata configuration options.
20624  * @param {Object} recordType Either an Array of field definition objects
20625  * as specified to {@link Roo.data.Record#create},
20626  * or an {@link Roo.data.Record} object
20627  * created using {@link Roo.data.Record#create}.
20628  */
20629 Roo.data.ArrayReader = function(meta, recordType){
20630     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20631 };
20632
20633 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20634     /**
20635      * Create a data block containing Roo.data.Records from an XML document.
20636      * @param {Object} o An Array of row objects which represents the dataset.
20637      * @return {Object} data A data block which is used by an Roo.data.Store object as
20638      * a cache of Roo.data.Records.
20639      */
20640     readRecords : function(o){
20641         var sid = this.meta ? this.meta.id : null;
20642         var recordType = this.recordType, fields = recordType.prototype.fields;
20643         var records = [];
20644         var root = o;
20645             for(var i = 0; i < root.length; i++){
20646                     var n = root[i];
20647                 var values = {};
20648                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20649                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20650                 var f = fields.items[j];
20651                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20652                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20653                 v = f.convert(v);
20654                 values[f.name] = v;
20655             }
20656                 var record = new recordType(values, id);
20657                 record.json = n;
20658                 records[records.length] = record;
20659             }
20660             return {
20661                 records : records,
20662                 totalRecords : records.length
20663             };
20664     }
20665 });/*
20666  * Based on:
20667  * Ext JS Library 1.1.1
20668  * Copyright(c) 2006-2007, Ext JS, LLC.
20669  *
20670  * Originally Released Under LGPL - original licence link has changed is not relivant.
20671  *
20672  * Fork - LGPL
20673  * <script type="text/javascript">
20674  */
20675
20676
20677 /**
20678  * @class Roo.data.Tree
20679  * @extends Roo.util.Observable
20680  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20681  * in the tree have most standard DOM functionality.
20682  * @constructor
20683  * @param {Node} root (optional) The root node
20684  */
20685 Roo.data.Tree = function(root){
20686    this.nodeHash = {};
20687    /**
20688     * The root node for this tree
20689     * @type Node
20690     */
20691    this.root = null;
20692    if(root){
20693        this.setRootNode(root);
20694    }
20695    this.addEvents({
20696        /**
20697         * @event append
20698         * Fires when a new child node is appended to a node in this tree.
20699         * @param {Tree} tree The owner tree
20700         * @param {Node} parent The parent node
20701         * @param {Node} node The newly appended node
20702         * @param {Number} index The index of the newly appended node
20703         */
20704        "append" : true,
20705        /**
20706         * @event remove
20707         * Fires when a child node is removed from a node in this tree.
20708         * @param {Tree} tree The owner tree
20709         * @param {Node} parent The parent node
20710         * @param {Node} node The child node removed
20711         */
20712        "remove" : true,
20713        /**
20714         * @event move
20715         * Fires when a node is moved to a new location in the tree
20716         * @param {Tree} tree The owner tree
20717         * @param {Node} node The node moved
20718         * @param {Node} oldParent The old parent of this node
20719         * @param {Node} newParent The new parent of this node
20720         * @param {Number} index The index it was moved to
20721         */
20722        "move" : true,
20723        /**
20724         * @event insert
20725         * Fires when a new child node is inserted in a node in this tree.
20726         * @param {Tree} tree The owner tree
20727         * @param {Node} parent The parent node
20728         * @param {Node} node The child node inserted
20729         * @param {Node} refNode The child node the node was inserted before
20730         */
20731        "insert" : true,
20732        /**
20733         * @event beforeappend
20734         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20735         * @param {Tree} tree The owner tree
20736         * @param {Node} parent The parent node
20737         * @param {Node} node The child node to be appended
20738         */
20739        "beforeappend" : true,
20740        /**
20741         * @event beforeremove
20742         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20743         * @param {Tree} tree The owner tree
20744         * @param {Node} parent The parent node
20745         * @param {Node} node The child node to be removed
20746         */
20747        "beforeremove" : true,
20748        /**
20749         * @event beforemove
20750         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20751         * @param {Tree} tree The owner tree
20752         * @param {Node} node The node being moved
20753         * @param {Node} oldParent The parent of the node
20754         * @param {Node} newParent The new parent the node is moving to
20755         * @param {Number} index The index it is being moved to
20756         */
20757        "beforemove" : true,
20758        /**
20759         * @event beforeinsert
20760         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20761         * @param {Tree} tree The owner tree
20762         * @param {Node} parent The parent node
20763         * @param {Node} node The child node to be inserted
20764         * @param {Node} refNode The child node the node is being inserted before
20765         */
20766        "beforeinsert" : true
20767    });
20768
20769     Roo.data.Tree.superclass.constructor.call(this);
20770 };
20771
20772 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20773     pathSeparator: "/",
20774
20775     proxyNodeEvent : function(){
20776         return this.fireEvent.apply(this, arguments);
20777     },
20778
20779     /**
20780      * Returns the root node for this tree.
20781      * @return {Node}
20782      */
20783     getRootNode : function(){
20784         return this.root;
20785     },
20786
20787     /**
20788      * Sets the root node for this tree.
20789      * @param {Node} node
20790      * @return {Node}
20791      */
20792     setRootNode : function(node){
20793         this.root = node;
20794         node.ownerTree = this;
20795         node.isRoot = true;
20796         this.registerNode(node);
20797         return node;
20798     },
20799
20800     /**
20801      * Gets a node in this tree by its id.
20802      * @param {String} id
20803      * @return {Node}
20804      */
20805     getNodeById : function(id){
20806         return this.nodeHash[id];
20807     },
20808
20809     registerNode : function(node){
20810         this.nodeHash[node.id] = node;
20811     },
20812
20813     unregisterNode : function(node){
20814         delete this.nodeHash[node.id];
20815     },
20816
20817     toString : function(){
20818         return "[Tree"+(this.id?" "+this.id:"")+"]";
20819     }
20820 });
20821
20822 /**
20823  * @class Roo.data.Node
20824  * @extends Roo.util.Observable
20825  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20826  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20827  * @constructor
20828  * @param {Object} attributes The attributes/config for the node
20829  */
20830 Roo.data.Node = function(attributes){
20831     /**
20832      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20833      * @type {Object}
20834      */
20835     this.attributes = attributes || {};
20836     this.leaf = this.attributes.leaf;
20837     /**
20838      * The node id. @type String
20839      */
20840     this.id = this.attributes.id;
20841     if(!this.id){
20842         this.id = Roo.id(null, "ynode-");
20843         this.attributes.id = this.id;
20844     }
20845     /**
20846      * All child nodes of this node. @type Array
20847      */
20848     this.childNodes = [];
20849     if(!this.childNodes.indexOf){ // indexOf is a must
20850         this.childNodes.indexOf = function(o){
20851             for(var i = 0, len = this.length; i < len; i++){
20852                 if(this[i] == o) return i;
20853             }
20854             return -1;
20855         };
20856     }
20857     /**
20858      * The parent node for this node. @type Node
20859      */
20860     this.parentNode = null;
20861     /**
20862      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20863      */
20864     this.firstChild = null;
20865     /**
20866      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20867      */
20868     this.lastChild = null;
20869     /**
20870      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20871      */
20872     this.previousSibling = null;
20873     /**
20874      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20875      */
20876     this.nextSibling = null;
20877
20878     this.addEvents({
20879        /**
20880         * @event append
20881         * Fires when a new child node is appended
20882         * @param {Tree} tree The owner tree
20883         * @param {Node} this This node
20884         * @param {Node} node The newly appended node
20885         * @param {Number} index The index of the newly appended node
20886         */
20887        "append" : true,
20888        /**
20889         * @event remove
20890         * Fires when a child node is removed
20891         * @param {Tree} tree The owner tree
20892         * @param {Node} this This node
20893         * @param {Node} node The removed node
20894         */
20895        "remove" : true,
20896        /**
20897         * @event move
20898         * Fires when this node is moved to a new location in the tree
20899         * @param {Tree} tree The owner tree
20900         * @param {Node} this This node
20901         * @param {Node} oldParent The old parent of this node
20902         * @param {Node} newParent The new parent of this node
20903         * @param {Number} index The index it was moved to
20904         */
20905        "move" : true,
20906        /**
20907         * @event insert
20908         * Fires when a new child node is inserted.
20909         * @param {Tree} tree The owner tree
20910         * @param {Node} this This node
20911         * @param {Node} node The child node inserted
20912         * @param {Node} refNode The child node the node was inserted before
20913         */
20914        "insert" : true,
20915        /**
20916         * @event beforeappend
20917         * Fires before a new child is appended, return false to cancel the append.
20918         * @param {Tree} tree The owner tree
20919         * @param {Node} this This node
20920         * @param {Node} node The child node to be appended
20921         */
20922        "beforeappend" : true,
20923        /**
20924         * @event beforeremove
20925         * Fires before a child is removed, return false to cancel the remove.
20926         * @param {Tree} tree The owner tree
20927         * @param {Node} this This node
20928         * @param {Node} node The child node to be removed
20929         */
20930        "beforeremove" : true,
20931        /**
20932         * @event beforemove
20933         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20934         * @param {Tree} tree The owner tree
20935         * @param {Node} this This node
20936         * @param {Node} oldParent The parent of this node
20937         * @param {Node} newParent The new parent this node is moving to
20938         * @param {Number} index The index it is being moved to
20939         */
20940        "beforemove" : true,
20941        /**
20942         * @event beforeinsert
20943         * Fires before a new child is inserted, return false to cancel the insert.
20944         * @param {Tree} tree The owner tree
20945         * @param {Node} this This node
20946         * @param {Node} node The child node to be inserted
20947         * @param {Node} refNode The child node the node is being inserted before
20948         */
20949        "beforeinsert" : true
20950    });
20951     this.listeners = this.attributes.listeners;
20952     Roo.data.Node.superclass.constructor.call(this);
20953 };
20954
20955 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20956     fireEvent : function(evtName){
20957         // first do standard event for this node
20958         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20959             return false;
20960         }
20961         // then bubble it up to the tree if the event wasn't cancelled
20962         var ot = this.getOwnerTree();
20963         if(ot){
20964             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20965                 return false;
20966             }
20967         }
20968         return true;
20969     },
20970
20971     /**
20972      * Returns true if this node is a leaf
20973      * @return {Boolean}
20974      */
20975     isLeaf : function(){
20976         return this.leaf === true;
20977     },
20978
20979     // private
20980     setFirstChild : function(node){
20981         this.firstChild = node;
20982     },
20983
20984     //private
20985     setLastChild : function(node){
20986         this.lastChild = node;
20987     },
20988
20989
20990     /**
20991      * Returns true if this node is the last child of its parent
20992      * @return {Boolean}
20993      */
20994     isLast : function(){
20995        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20996     },
20997
20998     /**
20999      * Returns true if this node is the first child of its parent
21000      * @return {Boolean}
21001      */
21002     isFirst : function(){
21003        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21004     },
21005
21006     hasChildNodes : function(){
21007         return !this.isLeaf() && this.childNodes.length > 0;
21008     },
21009
21010     /**
21011      * Insert node(s) as the last child node of this node.
21012      * @param {Node/Array} node The node or Array of nodes to append
21013      * @return {Node} The appended node if single append, or null if an array was passed
21014      */
21015     appendChild : function(node){
21016         var multi = false;
21017         if(node instanceof Array){
21018             multi = node;
21019         }else if(arguments.length > 1){
21020             multi = arguments;
21021         }
21022         // if passed an array or multiple args do them one by one
21023         if(multi){
21024             for(var i = 0, len = multi.length; i < len; i++) {
21025                 this.appendChild(multi[i]);
21026             }
21027         }else{
21028             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21029                 return false;
21030             }
21031             var index = this.childNodes.length;
21032             var oldParent = node.parentNode;
21033             // it's a move, make sure we move it cleanly
21034             if(oldParent){
21035                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21036                     return false;
21037                 }
21038                 oldParent.removeChild(node);
21039             }
21040             index = this.childNodes.length;
21041             if(index == 0){
21042                 this.setFirstChild(node);
21043             }
21044             this.childNodes.push(node);
21045             node.parentNode = this;
21046             var ps = this.childNodes[index-1];
21047             if(ps){
21048                 node.previousSibling = ps;
21049                 ps.nextSibling = node;
21050             }else{
21051                 node.previousSibling = null;
21052             }
21053             node.nextSibling = null;
21054             this.setLastChild(node);
21055             node.setOwnerTree(this.getOwnerTree());
21056             this.fireEvent("append", this.ownerTree, this, node, index);
21057             if(oldParent){
21058                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21059             }
21060             return node;
21061         }
21062     },
21063
21064     /**
21065      * Removes a child node from this node.
21066      * @param {Node} node The node to remove
21067      * @return {Node} The removed node
21068      */
21069     removeChild : function(node){
21070         var index = this.childNodes.indexOf(node);
21071         if(index == -1){
21072             return false;
21073         }
21074         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21075             return false;
21076         }
21077
21078         // remove it from childNodes collection
21079         this.childNodes.splice(index, 1);
21080
21081         // update siblings
21082         if(node.previousSibling){
21083             node.previousSibling.nextSibling = node.nextSibling;
21084         }
21085         if(node.nextSibling){
21086             node.nextSibling.previousSibling = node.previousSibling;
21087         }
21088
21089         // update child refs
21090         if(this.firstChild == node){
21091             this.setFirstChild(node.nextSibling);
21092         }
21093         if(this.lastChild == node){
21094             this.setLastChild(node.previousSibling);
21095         }
21096
21097         node.setOwnerTree(null);
21098         // clear any references from the node
21099         node.parentNode = null;
21100         node.previousSibling = null;
21101         node.nextSibling = null;
21102         this.fireEvent("remove", this.ownerTree, this, node);
21103         return node;
21104     },
21105
21106     /**
21107      * Inserts the first node before the second node in this nodes childNodes collection.
21108      * @param {Node} node The node to insert
21109      * @param {Node} refNode The node to insert before (if null the node is appended)
21110      * @return {Node} The inserted node
21111      */
21112     insertBefore : function(node, refNode){
21113         if(!refNode){ // like standard Dom, refNode can be null for append
21114             return this.appendChild(node);
21115         }
21116         // nothing to do
21117         if(node == refNode){
21118             return false;
21119         }
21120
21121         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21122             return false;
21123         }
21124         var index = this.childNodes.indexOf(refNode);
21125         var oldParent = node.parentNode;
21126         var refIndex = index;
21127
21128         // when moving internally, indexes will change after remove
21129         if(oldParent == this && this.childNodes.indexOf(node) < index){
21130             refIndex--;
21131         }
21132
21133         // it's a move, make sure we move it cleanly
21134         if(oldParent){
21135             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21136                 return false;
21137             }
21138             oldParent.removeChild(node);
21139         }
21140         if(refIndex == 0){
21141             this.setFirstChild(node);
21142         }
21143         this.childNodes.splice(refIndex, 0, node);
21144         node.parentNode = this;
21145         var ps = this.childNodes[refIndex-1];
21146         if(ps){
21147             node.previousSibling = ps;
21148             ps.nextSibling = node;
21149         }else{
21150             node.previousSibling = null;
21151         }
21152         node.nextSibling = refNode;
21153         refNode.previousSibling = node;
21154         node.setOwnerTree(this.getOwnerTree());
21155         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21156         if(oldParent){
21157             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21158         }
21159         return node;
21160     },
21161
21162     /**
21163      * Returns the child node at the specified index.
21164      * @param {Number} index
21165      * @return {Node}
21166      */
21167     item : function(index){
21168         return this.childNodes[index];
21169     },
21170
21171     /**
21172      * Replaces one child node in this node with another.
21173      * @param {Node} newChild The replacement node
21174      * @param {Node} oldChild The node to replace
21175      * @return {Node} The replaced node
21176      */
21177     replaceChild : function(newChild, oldChild){
21178         this.insertBefore(newChild, oldChild);
21179         this.removeChild(oldChild);
21180         return oldChild;
21181     },
21182
21183     /**
21184      * Returns the index of a child node
21185      * @param {Node} node
21186      * @return {Number} The index of the node or -1 if it was not found
21187      */
21188     indexOf : function(child){
21189         return this.childNodes.indexOf(child);
21190     },
21191
21192     /**
21193      * Returns the tree this node is in.
21194      * @return {Tree}
21195      */
21196     getOwnerTree : function(){
21197         // if it doesn't have one, look for one
21198         if(!this.ownerTree){
21199             var p = this;
21200             while(p){
21201                 if(p.ownerTree){
21202                     this.ownerTree = p.ownerTree;
21203                     break;
21204                 }
21205                 p = p.parentNode;
21206             }
21207         }
21208         return this.ownerTree;
21209     },
21210
21211     /**
21212      * Returns depth of this node (the root node has a depth of 0)
21213      * @return {Number}
21214      */
21215     getDepth : function(){
21216         var depth = 0;
21217         var p = this;
21218         while(p.parentNode){
21219             ++depth;
21220             p = p.parentNode;
21221         }
21222         return depth;
21223     },
21224
21225     // private
21226     setOwnerTree : function(tree){
21227         // if it's move, we need to update everyone
21228         if(tree != this.ownerTree){
21229             if(this.ownerTree){
21230                 this.ownerTree.unregisterNode(this);
21231             }
21232             this.ownerTree = tree;
21233             var cs = this.childNodes;
21234             for(var i = 0, len = cs.length; i < len; i++) {
21235                 cs[i].setOwnerTree(tree);
21236             }
21237             if(tree){
21238                 tree.registerNode(this);
21239             }
21240         }
21241     },
21242
21243     /**
21244      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21245      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21246      * @return {String} The path
21247      */
21248     getPath : function(attr){
21249         attr = attr || "id";
21250         var p = this.parentNode;
21251         var b = [this.attributes[attr]];
21252         while(p){
21253             b.unshift(p.attributes[attr]);
21254             p = p.parentNode;
21255         }
21256         var sep = this.getOwnerTree().pathSeparator;
21257         return sep + b.join(sep);
21258     },
21259
21260     /**
21261      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21262      * function call will be the scope provided or the current node. The arguments to the function
21263      * will be the args provided or the current node. If the function returns false at any point,
21264      * the bubble is stopped.
21265      * @param {Function} fn The function to call
21266      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21267      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21268      */
21269     bubble : function(fn, scope, args){
21270         var p = this;
21271         while(p){
21272             if(fn.call(scope || p, args || p) === false){
21273                 break;
21274             }
21275             p = p.parentNode;
21276         }
21277     },
21278
21279     /**
21280      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21281      * function call will be the scope provided or the current node. The arguments to the function
21282      * will be the args provided or the current node. If the function returns false at any point,
21283      * the cascade is stopped on that branch.
21284      * @param {Function} fn The function to call
21285      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21286      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21287      */
21288     cascade : function(fn, scope, args){
21289         if(fn.call(scope || this, args || this) !== false){
21290             var cs = this.childNodes;
21291             for(var i = 0, len = cs.length; i < len; i++) {
21292                 cs[i].cascade(fn, scope, args);
21293             }
21294         }
21295     },
21296
21297     /**
21298      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21299      * function call will be the scope provided or the current node. The arguments to the function
21300      * will be the args provided or the current node. If the function returns false at any point,
21301      * the iteration stops.
21302      * @param {Function} fn The function to call
21303      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21304      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21305      */
21306     eachChild : function(fn, scope, args){
21307         var cs = this.childNodes;
21308         for(var i = 0, len = cs.length; i < len; i++) {
21309                 if(fn.call(scope || this, args || cs[i]) === false){
21310                     break;
21311                 }
21312         }
21313     },
21314
21315     /**
21316      * Finds the first child that has the attribute with the specified value.
21317      * @param {String} attribute The attribute name
21318      * @param {Mixed} value The value to search for
21319      * @return {Node} The found child or null if none was found
21320      */
21321     findChild : function(attribute, value){
21322         var cs = this.childNodes;
21323         for(var i = 0, len = cs.length; i < len; i++) {
21324                 if(cs[i].attributes[attribute] == value){
21325                     return cs[i];
21326                 }
21327         }
21328         return null;
21329     },
21330
21331     /**
21332      * Finds the first child by a custom function. The child matches if the function passed
21333      * returns true.
21334      * @param {Function} fn
21335      * @param {Object} scope (optional)
21336      * @return {Node} The found child or null if none was found
21337      */
21338     findChildBy : function(fn, scope){
21339         var cs = this.childNodes;
21340         for(var i = 0, len = cs.length; i < len; i++) {
21341                 if(fn.call(scope||cs[i], cs[i]) === true){
21342                     return cs[i];
21343                 }
21344         }
21345         return null;
21346     },
21347
21348     /**
21349      * Sorts this nodes children using the supplied sort function
21350      * @param {Function} fn
21351      * @param {Object} scope (optional)
21352      */
21353     sort : function(fn, scope){
21354         var cs = this.childNodes;
21355         var len = cs.length;
21356         if(len > 0){
21357             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21358             cs.sort(sortFn);
21359             for(var i = 0; i < len; i++){
21360                 var n = cs[i];
21361                 n.previousSibling = cs[i-1];
21362                 n.nextSibling = cs[i+1];
21363                 if(i == 0){
21364                     this.setFirstChild(n);
21365                 }
21366                 if(i == len-1){
21367                     this.setLastChild(n);
21368                 }
21369             }
21370         }
21371     },
21372
21373     /**
21374      * Returns true if this node is an ancestor (at any point) of the passed node.
21375      * @param {Node} node
21376      * @return {Boolean}
21377      */
21378     contains : function(node){
21379         return node.isAncestor(this);
21380     },
21381
21382     /**
21383      * Returns true if the passed node is an ancestor (at any point) of this node.
21384      * @param {Node} node
21385      * @return {Boolean}
21386      */
21387     isAncestor : function(node){
21388         var p = this.parentNode;
21389         while(p){
21390             if(p == node){
21391                 return true;
21392             }
21393             p = p.parentNode;
21394         }
21395         return false;
21396     },
21397
21398     toString : function(){
21399         return "[Node"+(this.id?" "+this.id:"")+"]";
21400     }
21401 });/*
21402  * Based on:
21403  * Ext JS Library 1.1.1
21404  * Copyright(c) 2006-2007, Ext JS, LLC.
21405  *
21406  * Originally Released Under LGPL - original licence link has changed is not relivant.
21407  *
21408  * Fork - LGPL
21409  * <script type="text/javascript">
21410  */
21411  
21412
21413 /**
21414  * @class Roo.ComponentMgr
21415  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21416  * @singleton
21417  */
21418 Roo.ComponentMgr = function(){
21419     var all = new Roo.util.MixedCollection();
21420
21421     return {
21422         /**
21423          * Registers a component.
21424          * @param {Roo.Component} c The component
21425          */
21426         register : function(c){
21427             all.add(c);
21428         },
21429
21430         /**
21431          * Unregisters a component.
21432          * @param {Roo.Component} c The component
21433          */
21434         unregister : function(c){
21435             all.remove(c);
21436         },
21437
21438         /**
21439          * Returns a component by id
21440          * @param {String} id The component id
21441          */
21442         get : function(id){
21443             return all.get(id);
21444         },
21445
21446         /**
21447          * Registers a function that will be called when a specified component is added to ComponentMgr
21448          * @param {String} id The component id
21449          * @param {Funtction} fn The callback function
21450          * @param {Object} scope The scope of the callback
21451          */
21452         onAvailable : function(id, fn, scope){
21453             all.on("add", function(index, o){
21454                 if(o.id == id){
21455                     fn.call(scope || o, o);
21456                     all.un("add", fn, scope);
21457                 }
21458             });
21459         }
21460     };
21461 }();/*
21462  * Based on:
21463  * Ext JS Library 1.1.1
21464  * Copyright(c) 2006-2007, Ext JS, LLC.
21465  *
21466  * Originally Released Under LGPL - original licence link has changed is not relivant.
21467  *
21468  * Fork - LGPL
21469  * <script type="text/javascript">
21470  */
21471  
21472 /**
21473  * @class Roo.Component
21474  * @extends Roo.util.Observable
21475  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21476  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21477  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21478  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21479  * All visual components (widgets) that require rendering into a layout should subclass Component.
21480  * @constructor
21481  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21482  * 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
21483  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21484  */
21485 Roo.Component = function(config){
21486     config = config || {};
21487     if(config.tagName || config.dom || typeof config == "string"){ // element object
21488         config = {el: config, id: config.id || config};
21489     }
21490     this.initialConfig = config;
21491
21492     Roo.apply(this, config);
21493     this.addEvents({
21494         /**
21495          * @event disable
21496          * Fires after the component is disabled.
21497              * @param {Roo.Component} this
21498              */
21499         disable : true,
21500         /**
21501          * @event enable
21502          * Fires after the component is enabled.
21503              * @param {Roo.Component} this
21504              */
21505         enable : true,
21506         /**
21507          * @event beforeshow
21508          * Fires before the component is shown.  Return false to stop the show.
21509              * @param {Roo.Component} this
21510              */
21511         beforeshow : true,
21512         /**
21513          * @event show
21514          * Fires after the component is shown.
21515              * @param {Roo.Component} this
21516              */
21517         show : true,
21518         /**
21519          * @event beforehide
21520          * Fires before the component is hidden. Return false to stop the hide.
21521              * @param {Roo.Component} this
21522              */
21523         beforehide : true,
21524         /**
21525          * @event hide
21526          * Fires after the component is hidden.
21527              * @param {Roo.Component} this
21528              */
21529         hide : true,
21530         /**
21531          * @event beforerender
21532          * Fires before the component is rendered. Return false to stop the render.
21533              * @param {Roo.Component} this
21534              */
21535         beforerender : true,
21536         /**
21537          * @event render
21538          * Fires after the component is rendered.
21539              * @param {Roo.Component} this
21540              */
21541         render : true,
21542         /**
21543          * @event beforedestroy
21544          * Fires before the component is destroyed. Return false to stop the destroy.
21545              * @param {Roo.Component} this
21546              */
21547         beforedestroy : true,
21548         /**
21549          * @event destroy
21550          * Fires after the component is destroyed.
21551              * @param {Roo.Component} this
21552              */
21553         destroy : true
21554     });
21555     if(!this.id){
21556         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21557     }
21558     Roo.ComponentMgr.register(this);
21559     Roo.Component.superclass.constructor.call(this);
21560     this.initComponent();
21561     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21562         this.render(this.renderTo);
21563         delete this.renderTo;
21564     }
21565 };
21566
21567 // private
21568 Roo.Component.AUTO_ID = 1000;
21569
21570 Roo.extend(Roo.Component, Roo.util.Observable, {
21571     /**
21572      * @property {Boolean} hidden
21573      * true if this component is hidden. Read-only.
21574      */
21575     hidden : false,
21576     /**
21577      * true if this component is disabled. Read-only.
21578      */
21579     disabled : false,
21580     /**
21581      * true if this component has been rendered. Read-only.
21582      */
21583     rendered : false,
21584     
21585     /** @cfg {String} disableClass
21586      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21587      */
21588     disabledClass : "x-item-disabled",
21589         /** @cfg {Boolean} allowDomMove
21590          * Whether the component can move the Dom node when rendering (defaults to true).
21591          */
21592     allowDomMove : true,
21593     /** @cfg {String} hideMode
21594      * How this component should hidden. Supported values are
21595      * "visibility" (css visibility), "offsets" (negative offset position) and
21596      * "display" (css display) - defaults to "display".
21597      */
21598     hideMode: 'display',
21599
21600     // private
21601     ctype : "Roo.Component",
21602
21603     /** @cfg {String} actionMode 
21604      * which property holds the element that used for  hide() / show() / disable() / enable()
21605      * default is 'el' 
21606      */
21607     actionMode : "el",
21608
21609     // private
21610     getActionEl : function(){
21611         return this[this.actionMode];
21612     },
21613
21614     initComponent : Roo.emptyFn,
21615     /**
21616      * If this is a lazy rendering component, render it to its container element.
21617      * @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.
21618      */
21619     render : function(container, position){
21620         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21621             if(!container && this.el){
21622                 this.el = Roo.get(this.el);
21623                 container = this.el.dom.parentNode;
21624                 this.allowDomMove = false;
21625             }
21626             this.container = Roo.get(container);
21627             this.rendered = true;
21628             if(position !== undefined){
21629                 if(typeof position == 'number'){
21630                     position = this.container.dom.childNodes[position];
21631                 }else{
21632                     position = Roo.getDom(position);
21633                 }
21634             }
21635             this.onRender(this.container, position || null);
21636             if(this.cls){
21637                 this.el.addClass(this.cls);
21638                 delete this.cls;
21639             }
21640             if(this.style){
21641                 this.el.applyStyles(this.style);
21642                 delete this.style;
21643             }
21644             this.fireEvent("render", this);
21645             this.afterRender(this.container);
21646             if(this.hidden){
21647                 this.hide();
21648             }
21649             if(this.disabled){
21650                 this.disable();
21651             }
21652         }
21653         return this;
21654     },
21655
21656     // private
21657     // default function is not really useful
21658     onRender : function(ct, position){
21659         if(this.el){
21660             this.el = Roo.get(this.el);
21661             if(this.allowDomMove !== false){
21662                 ct.dom.insertBefore(this.el.dom, position);
21663             }
21664         }
21665     },
21666
21667     // private
21668     getAutoCreate : function(){
21669         var cfg = typeof this.autoCreate == "object" ?
21670                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21671         if(this.id && !cfg.id){
21672             cfg.id = this.id;
21673         }
21674         return cfg;
21675     },
21676
21677     // private
21678     afterRender : Roo.emptyFn,
21679
21680     /**
21681      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21682      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21683      */
21684     destroy : function(){
21685         if(this.fireEvent("beforedestroy", this) !== false){
21686             this.purgeListeners();
21687             this.beforeDestroy();
21688             if(this.rendered){
21689                 this.el.removeAllListeners();
21690                 this.el.remove();
21691                 if(this.actionMode == "container"){
21692                     this.container.remove();
21693                 }
21694             }
21695             this.onDestroy();
21696             Roo.ComponentMgr.unregister(this);
21697             this.fireEvent("destroy", this);
21698         }
21699     },
21700
21701         // private
21702     beforeDestroy : function(){
21703
21704     },
21705
21706         // private
21707         onDestroy : function(){
21708
21709     },
21710
21711     /**
21712      * Returns the underlying {@link Roo.Element}.
21713      * @return {Roo.Element} The element
21714      */
21715     getEl : function(){
21716         return this.el;
21717     },
21718
21719     /**
21720      * Returns the id of this component.
21721      * @return {String}
21722      */
21723     getId : function(){
21724         return this.id;
21725     },
21726
21727     /**
21728      * Try to focus this component.
21729      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21730      * @return {Roo.Component} this
21731      */
21732     focus : function(selectText){
21733         if(this.rendered){
21734             this.el.focus();
21735             if(selectText === true){
21736                 this.el.dom.select();
21737             }
21738         }
21739         return this;
21740     },
21741
21742     // private
21743     blur : function(){
21744         if(this.rendered){
21745             this.el.blur();
21746         }
21747         return this;
21748     },
21749
21750     /**
21751      * Disable this component.
21752      * @return {Roo.Component} this
21753      */
21754     disable : function(){
21755         if(this.rendered){
21756             this.onDisable();
21757         }
21758         this.disabled = true;
21759         this.fireEvent("disable", this);
21760         return this;
21761     },
21762
21763         // private
21764     onDisable : function(){
21765         this.getActionEl().addClass(this.disabledClass);
21766         this.el.dom.disabled = true;
21767     },
21768
21769     /**
21770      * Enable this component.
21771      * @return {Roo.Component} this
21772      */
21773     enable : function(){
21774         if(this.rendered){
21775             this.onEnable();
21776         }
21777         this.disabled = false;
21778         this.fireEvent("enable", this);
21779         return this;
21780     },
21781
21782         // private
21783     onEnable : function(){
21784         this.getActionEl().removeClass(this.disabledClass);
21785         this.el.dom.disabled = false;
21786     },
21787
21788     /**
21789      * Convenience function for setting disabled/enabled by boolean.
21790      * @param {Boolean} disabled
21791      */
21792     setDisabled : function(disabled){
21793         this[disabled ? "disable" : "enable"]();
21794     },
21795
21796     /**
21797      * Show this component.
21798      * @return {Roo.Component} this
21799      */
21800     show: function(){
21801         if(this.fireEvent("beforeshow", this) !== false){
21802             this.hidden = false;
21803             if(this.rendered){
21804                 this.onShow();
21805             }
21806             this.fireEvent("show", this);
21807         }
21808         return this;
21809     },
21810
21811     // private
21812     onShow : function(){
21813         var ae = this.getActionEl();
21814         if(this.hideMode == 'visibility'){
21815             ae.dom.style.visibility = "visible";
21816         }else if(this.hideMode == 'offsets'){
21817             ae.removeClass('x-hidden');
21818         }else{
21819             ae.dom.style.display = "";
21820         }
21821     },
21822
21823     /**
21824      * Hide this component.
21825      * @return {Roo.Component} this
21826      */
21827     hide: function(){
21828         if(this.fireEvent("beforehide", this) !== false){
21829             this.hidden = true;
21830             if(this.rendered){
21831                 this.onHide();
21832             }
21833             this.fireEvent("hide", this);
21834         }
21835         return this;
21836     },
21837
21838     // private
21839     onHide : function(){
21840         var ae = this.getActionEl();
21841         if(this.hideMode == 'visibility'){
21842             ae.dom.style.visibility = "hidden";
21843         }else if(this.hideMode == 'offsets'){
21844             ae.addClass('x-hidden');
21845         }else{
21846             ae.dom.style.display = "none";
21847         }
21848     },
21849
21850     /**
21851      * Convenience function to hide or show this component by boolean.
21852      * @param {Boolean} visible True to show, false to hide
21853      * @return {Roo.Component} this
21854      */
21855     setVisible: function(visible){
21856         if(visible) {
21857             this.show();
21858         }else{
21859             this.hide();
21860         }
21861         return this;
21862     },
21863
21864     /**
21865      * Returns true if this component is visible.
21866      */
21867     isVisible : function(){
21868         return this.getActionEl().isVisible();
21869     },
21870
21871     cloneConfig : function(overrides){
21872         overrides = overrides || {};
21873         var id = overrides.id || Roo.id();
21874         var cfg = Roo.applyIf(overrides, this.initialConfig);
21875         cfg.id = id; // prevent dup id
21876         return new this.constructor(cfg);
21877     }
21878 });/*
21879  * Based on:
21880  * Ext JS Library 1.1.1
21881  * Copyright(c) 2006-2007, Ext JS, LLC.
21882  *
21883  * Originally Released Under LGPL - original licence link has changed is not relivant.
21884  *
21885  * Fork - LGPL
21886  * <script type="text/javascript">
21887  */
21888  (function(){ 
21889 /**
21890  * @class Roo.Layer
21891  * @extends Roo.Element
21892  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21893  * automatic maintaining of shadow/shim positions.
21894  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21895  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21896  * you can pass a string with a CSS class name. False turns off the shadow.
21897  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21898  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21899  * @cfg {String} cls CSS class to add to the element
21900  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21901  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21902  * @constructor
21903  * @param {Object} config An object with config options.
21904  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21905  */
21906
21907 Roo.Layer = function(config, existingEl){
21908     config = config || {};
21909     var dh = Roo.DomHelper;
21910     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21911     if(existingEl){
21912         this.dom = Roo.getDom(existingEl);
21913     }
21914     if(!this.dom){
21915         var o = config.dh || {tag: "div", cls: "x-layer"};
21916         this.dom = dh.append(pel, o);
21917     }
21918     if(config.cls){
21919         this.addClass(config.cls);
21920     }
21921     this.constrain = config.constrain !== false;
21922     this.visibilityMode = Roo.Element.VISIBILITY;
21923     if(config.id){
21924         this.id = this.dom.id = config.id;
21925     }else{
21926         this.id = Roo.id(this.dom);
21927     }
21928     this.zindex = config.zindex || this.getZIndex();
21929     this.position("absolute", this.zindex);
21930     if(config.shadow){
21931         this.shadowOffset = config.shadowOffset || 4;
21932         this.shadow = new Roo.Shadow({
21933             offset : this.shadowOffset,
21934             mode : config.shadow
21935         });
21936     }else{
21937         this.shadowOffset = 0;
21938     }
21939     this.useShim = config.shim !== false && Roo.useShims;
21940     this.useDisplay = config.useDisplay;
21941     this.hide();
21942 };
21943
21944 var supr = Roo.Element.prototype;
21945
21946 // shims are shared among layer to keep from having 100 iframes
21947 var shims = [];
21948
21949 Roo.extend(Roo.Layer, Roo.Element, {
21950
21951     getZIndex : function(){
21952         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21953     },
21954
21955     getShim : function(){
21956         if(!this.useShim){
21957             return null;
21958         }
21959         if(this.shim){
21960             return this.shim;
21961         }
21962         var shim = shims.shift();
21963         if(!shim){
21964             shim = this.createShim();
21965             shim.enableDisplayMode('block');
21966             shim.dom.style.display = 'none';
21967             shim.dom.style.visibility = 'visible';
21968         }
21969         var pn = this.dom.parentNode;
21970         if(shim.dom.parentNode != pn){
21971             pn.insertBefore(shim.dom, this.dom);
21972         }
21973         shim.setStyle('z-index', this.getZIndex()-2);
21974         this.shim = shim;
21975         return shim;
21976     },
21977
21978     hideShim : function(){
21979         if(this.shim){
21980             this.shim.setDisplayed(false);
21981             shims.push(this.shim);
21982             delete this.shim;
21983         }
21984     },
21985
21986     disableShadow : function(){
21987         if(this.shadow){
21988             this.shadowDisabled = true;
21989             this.shadow.hide();
21990             this.lastShadowOffset = this.shadowOffset;
21991             this.shadowOffset = 0;
21992         }
21993     },
21994
21995     enableShadow : function(show){
21996         if(this.shadow){
21997             this.shadowDisabled = false;
21998             this.shadowOffset = this.lastShadowOffset;
21999             delete this.lastShadowOffset;
22000             if(show){
22001                 this.sync(true);
22002             }
22003         }
22004     },
22005
22006     // private
22007     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22008     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22009     sync : function(doShow){
22010         var sw = this.shadow;
22011         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22012             var sh = this.getShim();
22013
22014             var w = this.getWidth(),
22015                 h = this.getHeight();
22016
22017             var l = this.getLeft(true),
22018                 t = this.getTop(true);
22019
22020             if(sw && !this.shadowDisabled){
22021                 if(doShow && !sw.isVisible()){
22022                     sw.show(this);
22023                 }else{
22024                     sw.realign(l, t, w, h);
22025                 }
22026                 if(sh){
22027                     if(doShow){
22028                        sh.show();
22029                     }
22030                     // fit the shim behind the shadow, so it is shimmed too
22031                     var a = sw.adjusts, s = sh.dom.style;
22032                     s.left = (Math.min(l, l+a.l))+"px";
22033                     s.top = (Math.min(t, t+a.t))+"px";
22034                     s.width = (w+a.w)+"px";
22035                     s.height = (h+a.h)+"px";
22036                 }
22037             }else if(sh){
22038                 if(doShow){
22039                    sh.show();
22040                 }
22041                 sh.setSize(w, h);
22042                 sh.setLeftTop(l, t);
22043             }
22044             
22045         }
22046     },
22047
22048     // private
22049     destroy : function(){
22050         this.hideShim();
22051         if(this.shadow){
22052             this.shadow.hide();
22053         }
22054         this.removeAllListeners();
22055         var pn = this.dom.parentNode;
22056         if(pn){
22057             pn.removeChild(this.dom);
22058         }
22059         Roo.Element.uncache(this.id);
22060     },
22061
22062     remove : function(){
22063         this.destroy();
22064     },
22065
22066     // private
22067     beginUpdate : function(){
22068         this.updating = true;
22069     },
22070
22071     // private
22072     endUpdate : function(){
22073         this.updating = false;
22074         this.sync(true);
22075     },
22076
22077     // private
22078     hideUnders : function(negOffset){
22079         if(this.shadow){
22080             this.shadow.hide();
22081         }
22082         this.hideShim();
22083     },
22084
22085     // private
22086     constrainXY : function(){
22087         if(this.constrain){
22088             var vw = Roo.lib.Dom.getViewWidth(),
22089                 vh = Roo.lib.Dom.getViewHeight();
22090             var s = Roo.get(document).getScroll();
22091
22092             var xy = this.getXY();
22093             var x = xy[0], y = xy[1];   
22094             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22095             // only move it if it needs it
22096             var moved = false;
22097             // first validate right/bottom
22098             if((x + w) > vw+s.left){
22099                 x = vw - w - this.shadowOffset;
22100                 moved = true;
22101             }
22102             if((y + h) > vh+s.top){
22103                 y = vh - h - this.shadowOffset;
22104                 moved = true;
22105             }
22106             // then make sure top/left isn't negative
22107             if(x < s.left){
22108                 x = s.left;
22109                 moved = true;
22110             }
22111             if(y < s.top){
22112                 y = s.top;
22113                 moved = true;
22114             }
22115             if(moved){
22116                 if(this.avoidY){
22117                     var ay = this.avoidY;
22118                     if(y <= ay && (y+h) >= ay){
22119                         y = ay-h-5;   
22120                     }
22121                 }
22122                 xy = [x, y];
22123                 this.storeXY(xy);
22124                 supr.setXY.call(this, xy);
22125                 this.sync();
22126             }
22127         }
22128     },
22129
22130     isVisible : function(){
22131         return this.visible;    
22132     },
22133
22134     // private
22135     showAction : function(){
22136         this.visible = true; // track visibility to prevent getStyle calls
22137         if(this.useDisplay === true){
22138             this.setDisplayed("");
22139         }else if(this.lastXY){
22140             supr.setXY.call(this, this.lastXY);
22141         }else if(this.lastLT){
22142             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22143         }
22144     },
22145
22146     // private
22147     hideAction : function(){
22148         this.visible = false;
22149         if(this.useDisplay === true){
22150             this.setDisplayed(false);
22151         }else{
22152             this.setLeftTop(-10000,-10000);
22153         }
22154     },
22155
22156     // overridden Element method
22157     setVisible : function(v, a, d, c, e){
22158         if(v){
22159             this.showAction();
22160         }
22161         if(a && v){
22162             var cb = function(){
22163                 this.sync(true);
22164                 if(c){
22165                     c();
22166                 }
22167             }.createDelegate(this);
22168             supr.setVisible.call(this, true, true, d, cb, e);
22169         }else{
22170             if(!v){
22171                 this.hideUnders(true);
22172             }
22173             var cb = c;
22174             if(a){
22175                 cb = function(){
22176                     this.hideAction();
22177                     if(c){
22178                         c();
22179                     }
22180                 }.createDelegate(this);
22181             }
22182             supr.setVisible.call(this, v, a, d, cb, e);
22183             if(v){
22184                 this.sync(true);
22185             }else if(!a){
22186                 this.hideAction();
22187             }
22188         }
22189     },
22190
22191     storeXY : function(xy){
22192         delete this.lastLT;
22193         this.lastXY = xy;
22194     },
22195
22196     storeLeftTop : function(left, top){
22197         delete this.lastXY;
22198         this.lastLT = [left, top];
22199     },
22200
22201     // private
22202     beforeFx : function(){
22203         this.beforeAction();
22204         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22205     },
22206
22207     // private
22208     afterFx : function(){
22209         Roo.Layer.superclass.afterFx.apply(this, arguments);
22210         this.sync(this.isVisible());
22211     },
22212
22213     // private
22214     beforeAction : function(){
22215         if(!this.updating && this.shadow){
22216             this.shadow.hide();
22217         }
22218     },
22219
22220     // overridden Element method
22221     setLeft : function(left){
22222         this.storeLeftTop(left, this.getTop(true));
22223         supr.setLeft.apply(this, arguments);
22224         this.sync();
22225     },
22226
22227     setTop : function(top){
22228         this.storeLeftTop(this.getLeft(true), top);
22229         supr.setTop.apply(this, arguments);
22230         this.sync();
22231     },
22232
22233     setLeftTop : function(left, top){
22234         this.storeLeftTop(left, top);
22235         supr.setLeftTop.apply(this, arguments);
22236         this.sync();
22237     },
22238
22239     setXY : function(xy, a, d, c, e){
22240         this.fixDisplay();
22241         this.beforeAction();
22242         this.storeXY(xy);
22243         var cb = this.createCB(c);
22244         supr.setXY.call(this, xy, a, d, cb, e);
22245         if(!a){
22246             cb();
22247         }
22248     },
22249
22250     // private
22251     createCB : function(c){
22252         var el = this;
22253         return function(){
22254             el.constrainXY();
22255             el.sync(true);
22256             if(c){
22257                 c();
22258             }
22259         };
22260     },
22261
22262     // overridden Element method
22263     setX : function(x, a, d, c, e){
22264         this.setXY([x, this.getY()], a, d, c, e);
22265     },
22266
22267     // overridden Element method
22268     setY : function(y, a, d, c, e){
22269         this.setXY([this.getX(), y], a, d, c, e);
22270     },
22271
22272     // overridden Element method
22273     setSize : function(w, h, a, d, c, e){
22274         this.beforeAction();
22275         var cb = this.createCB(c);
22276         supr.setSize.call(this, w, h, a, d, cb, e);
22277         if(!a){
22278             cb();
22279         }
22280     },
22281
22282     // overridden Element method
22283     setWidth : function(w, a, d, c, e){
22284         this.beforeAction();
22285         var cb = this.createCB(c);
22286         supr.setWidth.call(this, w, a, d, cb, e);
22287         if(!a){
22288             cb();
22289         }
22290     },
22291
22292     // overridden Element method
22293     setHeight : function(h, a, d, c, e){
22294         this.beforeAction();
22295         var cb = this.createCB(c);
22296         supr.setHeight.call(this, h, a, d, cb, e);
22297         if(!a){
22298             cb();
22299         }
22300     },
22301
22302     // overridden Element method
22303     setBounds : function(x, y, w, h, a, d, c, e){
22304         this.beforeAction();
22305         var cb = this.createCB(c);
22306         if(!a){
22307             this.storeXY([x, y]);
22308             supr.setXY.call(this, [x, y]);
22309             supr.setSize.call(this, w, h, a, d, cb, e);
22310             cb();
22311         }else{
22312             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22313         }
22314         return this;
22315     },
22316     
22317     /**
22318      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22319      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22320      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22321      * @param {Number} zindex The new z-index to set
22322      * @return {this} The Layer
22323      */
22324     setZIndex : function(zindex){
22325         this.zindex = zindex;
22326         this.setStyle("z-index", zindex + 2);
22327         if(this.shadow){
22328             this.shadow.setZIndex(zindex + 1);
22329         }
22330         if(this.shim){
22331             this.shim.setStyle("z-index", zindex);
22332         }
22333     }
22334 });
22335 })();/*
22336  * Based on:
22337  * Ext JS Library 1.1.1
22338  * Copyright(c) 2006-2007, Ext JS, LLC.
22339  *
22340  * Originally Released Under LGPL - original licence link has changed is not relivant.
22341  *
22342  * Fork - LGPL
22343  * <script type="text/javascript">
22344  */
22345
22346
22347 /**
22348  * @class Roo.Shadow
22349  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22350  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22351  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22352  * @constructor
22353  * Create a new Shadow
22354  * @param {Object} config The config object
22355  */
22356 Roo.Shadow = function(config){
22357     Roo.apply(this, config);
22358     if(typeof this.mode != "string"){
22359         this.mode = this.defaultMode;
22360     }
22361     var o = this.offset, a = {h: 0};
22362     var rad = Math.floor(this.offset/2);
22363     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22364         case "drop":
22365             a.w = 0;
22366             a.l = a.t = o;
22367             a.t -= 1;
22368             if(Roo.isIE){
22369                 a.l -= this.offset + rad;
22370                 a.t -= this.offset + rad;
22371                 a.w -= rad;
22372                 a.h -= rad;
22373                 a.t += 1;
22374             }
22375         break;
22376         case "sides":
22377             a.w = (o*2);
22378             a.l = -o;
22379             a.t = o-1;
22380             if(Roo.isIE){
22381                 a.l -= (this.offset - rad);
22382                 a.t -= this.offset + rad;
22383                 a.l += 1;
22384                 a.w -= (this.offset - rad)*2;
22385                 a.w -= rad + 1;
22386                 a.h -= 1;
22387             }
22388         break;
22389         case "frame":
22390             a.w = a.h = (o*2);
22391             a.l = a.t = -o;
22392             a.t += 1;
22393             a.h -= 2;
22394             if(Roo.isIE){
22395                 a.l -= (this.offset - rad);
22396                 a.t -= (this.offset - rad);
22397                 a.l += 1;
22398                 a.w -= (this.offset + rad + 1);
22399                 a.h -= (this.offset + rad);
22400                 a.h += 1;
22401             }
22402         break;
22403     };
22404
22405     this.adjusts = a;
22406 };
22407
22408 Roo.Shadow.prototype = {
22409     /**
22410      * @cfg {String} mode
22411      * The shadow display mode.  Supports the following options:<br />
22412      * sides: Shadow displays on both sides and bottom only<br />
22413      * frame: Shadow displays equally on all four sides<br />
22414      * drop: Traditional bottom-right drop shadow (default)
22415      */
22416     /**
22417      * @cfg {String} offset
22418      * The number of pixels to offset the shadow from the element (defaults to 4)
22419      */
22420     offset: 4,
22421
22422     // private
22423     defaultMode: "drop",
22424
22425     /**
22426      * Displays the shadow under the target element
22427      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22428      */
22429     show : function(target){
22430         target = Roo.get(target);
22431         if(!this.el){
22432             this.el = Roo.Shadow.Pool.pull();
22433             if(this.el.dom.nextSibling != target.dom){
22434                 this.el.insertBefore(target);
22435             }
22436         }
22437         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22438         if(Roo.isIE){
22439             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22440         }
22441         this.realign(
22442             target.getLeft(true),
22443             target.getTop(true),
22444             target.getWidth(),
22445             target.getHeight()
22446         );
22447         this.el.dom.style.display = "block";
22448     },
22449
22450     /**
22451      * Returns true if the shadow is visible, else false
22452      */
22453     isVisible : function(){
22454         return this.el ? true : false;  
22455     },
22456
22457     /**
22458      * Direct alignment when values are already available. Show must be called at least once before
22459      * calling this method to ensure it is initialized.
22460      * @param {Number} left The target element left position
22461      * @param {Number} top The target element top position
22462      * @param {Number} width The target element width
22463      * @param {Number} height The target element height
22464      */
22465     realign : function(l, t, w, h){
22466         if(!this.el){
22467             return;
22468         }
22469         var a = this.adjusts, d = this.el.dom, s = d.style;
22470         var iea = 0;
22471         s.left = (l+a.l)+"px";
22472         s.top = (t+a.t)+"px";
22473         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22474         if(s.width != sws || s.height != shs){
22475             s.width = sws;
22476             s.height = shs;
22477             if(!Roo.isIE){
22478                 var cn = d.childNodes;
22479                 var sww = Math.max(0, (sw-12))+"px";
22480                 cn[0].childNodes[1].style.width = sww;
22481                 cn[1].childNodes[1].style.width = sww;
22482                 cn[2].childNodes[1].style.width = sww;
22483                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22484             }
22485         }
22486     },
22487
22488     /**
22489      * Hides this shadow
22490      */
22491     hide : function(){
22492         if(this.el){
22493             this.el.dom.style.display = "none";
22494             Roo.Shadow.Pool.push(this.el);
22495             delete this.el;
22496         }
22497     },
22498
22499     /**
22500      * Adjust the z-index of this shadow
22501      * @param {Number} zindex The new z-index
22502      */
22503     setZIndex : function(z){
22504         this.zIndex = z;
22505         if(this.el){
22506             this.el.setStyle("z-index", z);
22507         }
22508     }
22509 };
22510
22511 // Private utility class that manages the internal Shadow cache
22512 Roo.Shadow.Pool = function(){
22513     var p = [];
22514     var markup = Roo.isIE ?
22515                  '<div class="x-ie-shadow"></div>' :
22516                  '<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>';
22517     return {
22518         pull : function(){
22519             var sh = p.shift();
22520             if(!sh){
22521                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22522                 sh.autoBoxAdjust = false;
22523             }
22524             return sh;
22525         },
22526
22527         push : function(sh){
22528             p.push(sh);
22529         }
22530     };
22531 }();/*
22532  * Based on:
22533  * Ext JS Library 1.1.1
22534  * Copyright(c) 2006-2007, Ext JS, LLC.
22535  *
22536  * Originally Released Under LGPL - original licence link has changed is not relivant.
22537  *
22538  * Fork - LGPL
22539  * <script type="text/javascript">
22540  */
22541
22542 /**
22543  * @class Roo.BoxComponent
22544  * @extends Roo.Component
22545  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22546  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22547  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22548  * layout containers.
22549  * @constructor
22550  * @param {Roo.Element/String/Object} config The configuration options.
22551  */
22552 Roo.BoxComponent = function(config){
22553     Roo.Component.call(this, config);
22554     this.addEvents({
22555         /**
22556          * @event resize
22557          * Fires after the component is resized.
22558              * @param {Roo.Component} this
22559              * @param {Number} adjWidth The box-adjusted width that was set
22560              * @param {Number} adjHeight The box-adjusted height that was set
22561              * @param {Number} rawWidth The width that was originally specified
22562              * @param {Number} rawHeight The height that was originally specified
22563              */
22564         resize : true,
22565         /**
22566          * @event move
22567          * Fires after the component is moved.
22568              * @param {Roo.Component} this
22569              * @param {Number} x The new x position
22570              * @param {Number} y The new y position
22571              */
22572         move : true
22573     });
22574 };
22575
22576 Roo.extend(Roo.BoxComponent, Roo.Component, {
22577     // private, set in afterRender to signify that the component has been rendered
22578     boxReady : false,
22579     // private, used to defer height settings to subclasses
22580     deferHeight: false,
22581     /** @cfg {Number} width
22582      * width (optional) size of component
22583      */
22584      /** @cfg {Number} height
22585      * height (optional) size of component
22586      */
22587      
22588     /**
22589      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22590      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22591      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22592      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22593      * @return {Roo.BoxComponent} this
22594      */
22595     setSize : function(w, h){
22596         // support for standard size objects
22597         if(typeof w == 'object'){
22598             h = w.height;
22599             w = w.width;
22600         }
22601         // not rendered
22602         if(!this.boxReady){
22603             this.width = w;
22604             this.height = h;
22605             return this;
22606         }
22607
22608         // prevent recalcs when not needed
22609         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22610             return this;
22611         }
22612         this.lastSize = {width: w, height: h};
22613
22614         var adj = this.adjustSize(w, h);
22615         var aw = adj.width, ah = adj.height;
22616         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22617             var rz = this.getResizeEl();
22618             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22619                 rz.setSize(aw, ah);
22620             }else if(!this.deferHeight && ah !== undefined){
22621                 rz.setHeight(ah);
22622             }else if(aw !== undefined){
22623                 rz.setWidth(aw);
22624             }
22625             this.onResize(aw, ah, w, h);
22626             this.fireEvent('resize', this, aw, ah, w, h);
22627         }
22628         return this;
22629     },
22630
22631     /**
22632      * Gets the current size of the component's underlying element.
22633      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22634      */
22635     getSize : function(){
22636         return this.el.getSize();
22637     },
22638
22639     /**
22640      * Gets the current XY position of the component's underlying element.
22641      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22642      * @return {Array} The XY position of the element (e.g., [100, 200])
22643      */
22644     getPosition : function(local){
22645         if(local === true){
22646             return [this.el.getLeft(true), this.el.getTop(true)];
22647         }
22648         return this.xy || this.el.getXY();
22649     },
22650
22651     /**
22652      * Gets the current box measurements of the component's underlying element.
22653      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22654      * @returns {Object} box An object in the format {x, y, width, height}
22655      */
22656     getBox : function(local){
22657         var s = this.el.getSize();
22658         if(local){
22659             s.x = this.el.getLeft(true);
22660             s.y = this.el.getTop(true);
22661         }else{
22662             var xy = this.xy || this.el.getXY();
22663             s.x = xy[0];
22664             s.y = xy[1];
22665         }
22666         return s;
22667     },
22668
22669     /**
22670      * Sets the current box measurements of the component's underlying element.
22671      * @param {Object} box An object in the format {x, y, width, height}
22672      * @returns {Roo.BoxComponent} this
22673      */
22674     updateBox : function(box){
22675         this.setSize(box.width, box.height);
22676         this.setPagePosition(box.x, box.y);
22677         return this;
22678     },
22679
22680     // protected
22681     getResizeEl : function(){
22682         return this.resizeEl || this.el;
22683     },
22684
22685     // protected
22686     getPositionEl : function(){
22687         return this.positionEl || this.el;
22688     },
22689
22690     /**
22691      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22692      * This method fires the move event.
22693      * @param {Number} left The new left
22694      * @param {Number} top The new top
22695      * @returns {Roo.BoxComponent} this
22696      */
22697     setPosition : function(x, y){
22698         this.x = x;
22699         this.y = y;
22700         if(!this.boxReady){
22701             return this;
22702         }
22703         var adj = this.adjustPosition(x, y);
22704         var ax = adj.x, ay = adj.y;
22705
22706         var el = this.getPositionEl();
22707         if(ax !== undefined || ay !== undefined){
22708             if(ax !== undefined && ay !== undefined){
22709                 el.setLeftTop(ax, ay);
22710             }else if(ax !== undefined){
22711                 el.setLeft(ax);
22712             }else if(ay !== undefined){
22713                 el.setTop(ay);
22714             }
22715             this.onPosition(ax, ay);
22716             this.fireEvent('move', this, ax, ay);
22717         }
22718         return this;
22719     },
22720
22721     /**
22722      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22723      * This method fires the move event.
22724      * @param {Number} x The new x position
22725      * @param {Number} y The new y position
22726      * @returns {Roo.BoxComponent} this
22727      */
22728     setPagePosition : function(x, y){
22729         this.pageX = x;
22730         this.pageY = y;
22731         if(!this.boxReady){
22732             return;
22733         }
22734         if(x === undefined || y === undefined){ // cannot translate undefined points
22735             return;
22736         }
22737         var p = this.el.translatePoints(x, y);
22738         this.setPosition(p.left, p.top);
22739         return this;
22740     },
22741
22742     // private
22743     onRender : function(ct, position){
22744         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22745         if(this.resizeEl){
22746             this.resizeEl = Roo.get(this.resizeEl);
22747         }
22748         if(this.positionEl){
22749             this.positionEl = Roo.get(this.positionEl);
22750         }
22751     },
22752
22753     // private
22754     afterRender : function(){
22755         Roo.BoxComponent.superclass.afterRender.call(this);
22756         this.boxReady = true;
22757         this.setSize(this.width, this.height);
22758         if(this.x || this.y){
22759             this.setPosition(this.x, this.y);
22760         }
22761         if(this.pageX || this.pageY){
22762             this.setPagePosition(this.pageX, this.pageY);
22763         }
22764     },
22765
22766     /**
22767      * Force the component's size to recalculate based on the underlying element's current height and width.
22768      * @returns {Roo.BoxComponent} this
22769      */
22770     syncSize : function(){
22771         delete this.lastSize;
22772         this.setSize(this.el.getWidth(), this.el.getHeight());
22773         return this;
22774     },
22775
22776     /**
22777      * Called after the component is resized, this method is empty by default but can be implemented by any
22778      * subclass that needs to perform custom logic after a resize occurs.
22779      * @param {Number} adjWidth The box-adjusted width that was set
22780      * @param {Number} adjHeight The box-adjusted height that was set
22781      * @param {Number} rawWidth The width that was originally specified
22782      * @param {Number} rawHeight The height that was originally specified
22783      */
22784     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22785
22786     },
22787
22788     /**
22789      * Called after the component is moved, this method is empty by default but can be implemented by any
22790      * subclass that needs to perform custom logic after a move occurs.
22791      * @param {Number} x The new x position
22792      * @param {Number} y The new y position
22793      */
22794     onPosition : function(x, y){
22795
22796     },
22797
22798     // private
22799     adjustSize : function(w, h){
22800         if(this.autoWidth){
22801             w = 'auto';
22802         }
22803         if(this.autoHeight){
22804             h = 'auto';
22805         }
22806         return {width : w, height: h};
22807     },
22808
22809     // private
22810     adjustPosition : function(x, y){
22811         return {x : x, y: y};
22812     }
22813 });/*
22814  * Based on:
22815  * Ext JS Library 1.1.1
22816  * Copyright(c) 2006-2007, Ext JS, LLC.
22817  *
22818  * Originally Released Under LGPL - original licence link has changed is not relivant.
22819  *
22820  * Fork - LGPL
22821  * <script type="text/javascript">
22822  */
22823
22824
22825 /**
22826  * @class Roo.SplitBar
22827  * @extends Roo.util.Observable
22828  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22829  * <br><br>
22830  * Usage:
22831  * <pre><code>
22832 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22833                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22834 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22835 split.minSize = 100;
22836 split.maxSize = 600;
22837 split.animate = true;
22838 split.on('moved', splitterMoved);
22839 </code></pre>
22840  * @constructor
22841  * Create a new SplitBar
22842  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22843  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22844  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22845  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22846                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22847                         position of the SplitBar).
22848  */
22849 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22850     
22851     /** @private */
22852     this.el = Roo.get(dragElement, true);
22853     this.el.dom.unselectable = "on";
22854     /** @private */
22855     this.resizingEl = Roo.get(resizingElement, true);
22856
22857     /**
22858      * @private
22859      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22860      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22861      * @type Number
22862      */
22863     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22864     
22865     /**
22866      * The minimum size of the resizing element. (Defaults to 0)
22867      * @type Number
22868      */
22869     this.minSize = 0;
22870     
22871     /**
22872      * The maximum size of the resizing element. (Defaults to 2000)
22873      * @type Number
22874      */
22875     this.maxSize = 2000;
22876     
22877     /**
22878      * Whether to animate the transition to the new size
22879      * @type Boolean
22880      */
22881     this.animate = false;
22882     
22883     /**
22884      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22885      * @type Boolean
22886      */
22887     this.useShim = false;
22888     
22889     /** @private */
22890     this.shim = null;
22891     
22892     if(!existingProxy){
22893         /** @private */
22894         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22895     }else{
22896         this.proxy = Roo.get(existingProxy).dom;
22897     }
22898     /** @private */
22899     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22900     
22901     /** @private */
22902     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22903     
22904     /** @private */
22905     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22906     
22907     /** @private */
22908     this.dragSpecs = {};
22909     
22910     /**
22911      * @private The adapter to use to positon and resize elements
22912      */
22913     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22914     this.adapter.init(this);
22915     
22916     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22917         /** @private */
22918         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22919         this.el.addClass("x-splitbar-h");
22920     }else{
22921         /** @private */
22922         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22923         this.el.addClass("x-splitbar-v");
22924     }
22925     
22926     this.addEvents({
22927         /**
22928          * @event resize
22929          * Fires when the splitter is moved (alias for {@link #event-moved})
22930          * @param {Roo.SplitBar} this
22931          * @param {Number} newSize the new width or height
22932          */
22933         "resize" : true,
22934         /**
22935          * @event moved
22936          * Fires when the splitter is moved
22937          * @param {Roo.SplitBar} this
22938          * @param {Number} newSize the new width or height
22939          */
22940         "moved" : true,
22941         /**
22942          * @event beforeresize
22943          * Fires before the splitter is dragged
22944          * @param {Roo.SplitBar} this
22945          */
22946         "beforeresize" : true,
22947
22948         "beforeapply" : true
22949     });
22950
22951     Roo.util.Observable.call(this);
22952 };
22953
22954 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22955     onStartProxyDrag : function(x, y){
22956         this.fireEvent("beforeresize", this);
22957         if(!this.overlay){
22958             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22959             o.unselectable();
22960             o.enableDisplayMode("block");
22961             // all splitbars share the same overlay
22962             Roo.SplitBar.prototype.overlay = o;
22963         }
22964         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22965         this.overlay.show();
22966         Roo.get(this.proxy).setDisplayed("block");
22967         var size = this.adapter.getElementSize(this);
22968         this.activeMinSize = this.getMinimumSize();;
22969         this.activeMaxSize = this.getMaximumSize();;
22970         var c1 = size - this.activeMinSize;
22971         var c2 = Math.max(this.activeMaxSize - size, 0);
22972         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22973             this.dd.resetConstraints();
22974             this.dd.setXConstraint(
22975                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
22976                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
22977             );
22978             this.dd.setYConstraint(0, 0);
22979         }else{
22980             this.dd.resetConstraints();
22981             this.dd.setXConstraint(0, 0);
22982             this.dd.setYConstraint(
22983                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
22984                 this.placement == Roo.SplitBar.TOP ? c2 : c1
22985             );
22986          }
22987         this.dragSpecs.startSize = size;
22988         this.dragSpecs.startPoint = [x, y];
22989         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
22990     },
22991     
22992     /** 
22993      * @private Called after the drag operation by the DDProxy
22994      */
22995     onEndProxyDrag : function(e){
22996         Roo.get(this.proxy).setDisplayed(false);
22997         var endPoint = Roo.lib.Event.getXY(e);
22998         if(this.overlay){
22999             this.overlay.hide();
23000         }
23001         var newSize;
23002         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23003             newSize = this.dragSpecs.startSize + 
23004                 (this.placement == Roo.SplitBar.LEFT ?
23005                     endPoint[0] - this.dragSpecs.startPoint[0] :
23006                     this.dragSpecs.startPoint[0] - endPoint[0]
23007                 );
23008         }else{
23009             newSize = this.dragSpecs.startSize + 
23010                 (this.placement == Roo.SplitBar.TOP ?
23011                     endPoint[1] - this.dragSpecs.startPoint[1] :
23012                     this.dragSpecs.startPoint[1] - endPoint[1]
23013                 );
23014         }
23015         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23016         if(newSize != this.dragSpecs.startSize){
23017             if(this.fireEvent('beforeapply', this, newSize) !== false){
23018                 this.adapter.setElementSize(this, newSize);
23019                 this.fireEvent("moved", this, newSize);
23020                 this.fireEvent("resize", this, newSize);
23021             }
23022         }
23023     },
23024     
23025     /**
23026      * Get the adapter this SplitBar uses
23027      * @return The adapter object
23028      */
23029     getAdapter : function(){
23030         return this.adapter;
23031     },
23032     
23033     /**
23034      * Set the adapter this SplitBar uses
23035      * @param {Object} adapter A SplitBar adapter object
23036      */
23037     setAdapter : function(adapter){
23038         this.adapter = adapter;
23039         this.adapter.init(this);
23040     },
23041     
23042     /**
23043      * Gets the minimum size for the resizing element
23044      * @return {Number} The minimum size
23045      */
23046     getMinimumSize : function(){
23047         return this.minSize;
23048     },
23049     
23050     /**
23051      * Sets the minimum size for the resizing element
23052      * @param {Number} minSize The minimum size
23053      */
23054     setMinimumSize : function(minSize){
23055         this.minSize = minSize;
23056     },
23057     
23058     /**
23059      * Gets the maximum size for the resizing element
23060      * @return {Number} The maximum size
23061      */
23062     getMaximumSize : function(){
23063         return this.maxSize;
23064     },
23065     
23066     /**
23067      * Sets the maximum size for the resizing element
23068      * @param {Number} maxSize The maximum size
23069      */
23070     setMaximumSize : function(maxSize){
23071         this.maxSize = maxSize;
23072     },
23073     
23074     /**
23075      * Sets the initialize size for the resizing element
23076      * @param {Number} size The initial size
23077      */
23078     setCurrentSize : function(size){
23079         var oldAnimate = this.animate;
23080         this.animate = false;
23081         this.adapter.setElementSize(this, size);
23082         this.animate = oldAnimate;
23083     },
23084     
23085     /**
23086      * Destroy this splitbar. 
23087      * @param {Boolean} removeEl True to remove the element
23088      */
23089     destroy : function(removeEl){
23090         if(this.shim){
23091             this.shim.remove();
23092         }
23093         this.dd.unreg();
23094         this.proxy.parentNode.removeChild(this.proxy);
23095         if(removeEl){
23096             this.el.remove();
23097         }
23098     }
23099 });
23100
23101 /**
23102  * @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.
23103  */
23104 Roo.SplitBar.createProxy = function(dir){
23105     var proxy = new Roo.Element(document.createElement("div"));
23106     proxy.unselectable();
23107     var cls = 'x-splitbar-proxy';
23108     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23109     document.body.appendChild(proxy.dom);
23110     return proxy.dom;
23111 };
23112
23113 /** 
23114  * @class Roo.SplitBar.BasicLayoutAdapter
23115  * Default Adapter. It assumes the splitter and resizing element are not positioned
23116  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23117  */
23118 Roo.SplitBar.BasicLayoutAdapter = function(){
23119 };
23120
23121 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23122     // do nothing for now
23123     init : function(s){
23124     
23125     },
23126     /**
23127      * Called before drag operations to get the current size of the resizing element. 
23128      * @param {Roo.SplitBar} s The SplitBar using this adapter
23129      */
23130      getElementSize : function(s){
23131         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23132             return s.resizingEl.getWidth();
23133         }else{
23134             return s.resizingEl.getHeight();
23135         }
23136     },
23137     
23138     /**
23139      * Called after drag operations to set the size of the resizing element.
23140      * @param {Roo.SplitBar} s The SplitBar using this adapter
23141      * @param {Number} newSize The new size to set
23142      * @param {Function} onComplete A function to be invoked when resizing is complete
23143      */
23144     setElementSize : function(s, newSize, onComplete){
23145         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23146             if(!s.animate){
23147                 s.resizingEl.setWidth(newSize);
23148                 if(onComplete){
23149                     onComplete(s, newSize);
23150                 }
23151             }else{
23152                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23153             }
23154         }else{
23155             
23156             if(!s.animate){
23157                 s.resizingEl.setHeight(newSize);
23158                 if(onComplete){
23159                     onComplete(s, newSize);
23160                 }
23161             }else{
23162                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23163             }
23164         }
23165     }
23166 };
23167
23168 /** 
23169  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23170  * @extends Roo.SplitBar.BasicLayoutAdapter
23171  * Adapter that  moves the splitter element to align with the resized sizing element. 
23172  * Used with an absolute positioned SplitBar.
23173  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23174  * document.body, make sure you assign an id to the body element.
23175  */
23176 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23177     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23178     this.container = Roo.get(container);
23179 };
23180
23181 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23182     init : function(s){
23183         this.basic.init(s);
23184     },
23185     
23186     getElementSize : function(s){
23187         return this.basic.getElementSize(s);
23188     },
23189     
23190     setElementSize : function(s, newSize, onComplete){
23191         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23192     },
23193     
23194     moveSplitter : function(s){
23195         var yes = Roo.SplitBar;
23196         switch(s.placement){
23197             case yes.LEFT:
23198                 s.el.setX(s.resizingEl.getRight());
23199                 break;
23200             case yes.RIGHT:
23201                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23202                 break;
23203             case yes.TOP:
23204                 s.el.setY(s.resizingEl.getBottom());
23205                 break;
23206             case yes.BOTTOM:
23207                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23208                 break;
23209         }
23210     }
23211 };
23212
23213 /**
23214  * Orientation constant - Create a vertical SplitBar
23215  * @static
23216  * @type Number
23217  */
23218 Roo.SplitBar.VERTICAL = 1;
23219
23220 /**
23221  * Orientation constant - Create a horizontal SplitBar
23222  * @static
23223  * @type Number
23224  */
23225 Roo.SplitBar.HORIZONTAL = 2;
23226
23227 /**
23228  * Placement constant - The resizing element is to the left of the splitter element
23229  * @static
23230  * @type Number
23231  */
23232 Roo.SplitBar.LEFT = 1;
23233
23234 /**
23235  * Placement constant - The resizing element is to the right of the splitter element
23236  * @static
23237  * @type Number
23238  */
23239 Roo.SplitBar.RIGHT = 2;
23240
23241 /**
23242  * Placement constant - The resizing element is positioned above the splitter element
23243  * @static
23244  * @type Number
23245  */
23246 Roo.SplitBar.TOP = 3;
23247
23248 /**
23249  * Placement constant - The resizing element is positioned under splitter element
23250  * @static
23251  * @type Number
23252  */
23253 Roo.SplitBar.BOTTOM = 4;
23254 /*
23255  * Based on:
23256  * Ext JS Library 1.1.1
23257  * Copyright(c) 2006-2007, Ext JS, LLC.
23258  *
23259  * Originally Released Under LGPL - original licence link has changed is not relivant.
23260  *
23261  * Fork - LGPL
23262  * <script type="text/javascript">
23263  */
23264
23265 /**
23266  * @class Roo.View
23267  * @extends Roo.util.Observable
23268  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23269  * This class also supports single and multi selection modes. <br>
23270  * Create a data model bound view:
23271  <pre><code>
23272  var store = new Roo.data.Store(...);
23273
23274  var view = new Roo.View({
23275     el : "my-element",
23276     template : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23277  
23278     singleSelect: true,
23279     selectedClass: "ydataview-selected",
23280     store: store
23281  });
23282
23283  // listen for node click?
23284  view.on("click", function(vw, index, node, e){
23285  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23286  });
23287
23288  // load XML data
23289  dataModel.load("foobar.xml");
23290  </code></pre>
23291  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23292  * <br><br>
23293  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23294  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23295  * 
23296  * Note: old style constructor is still suported (container, template, config)
23297  * 
23298  * @constructor
23299  * Create a new View
23300  * @param {Object} config The config object
23301  * 
23302  */
23303 Roo.View = function(config, depreciated_tpl, depreciated_config){
23304     
23305     if (typeof(depreciated_tpl) == 'undefined') {
23306         // new way.. - universal constructor.
23307         Roo.apply(this, config);
23308         this.el  = Roo.get(this.el);
23309     } else {
23310         // old format..
23311         this.el  = Roo.get(config);
23312         this.tpl = depreciated_tpl;
23313         Roo.apply(this, depreciated_config);
23314     }
23315      
23316     
23317     if(typeof(this.tpl) == "string"){
23318         this.tpl = new Roo.Template(this.tpl);
23319     } 
23320     
23321     
23322     this.tpl.compile();
23323    
23324
23325      
23326     /** @private */
23327     this.addEvents({
23328     /**
23329      * @event beforeclick
23330      * Fires before a click is processed. Returns false to cancel the default action.
23331      * @param {Roo.View} this
23332      * @param {Number} index The index of the target node
23333      * @param {HTMLElement} node The target node
23334      * @param {Roo.EventObject} e The raw event object
23335      */
23336         "beforeclick" : true,
23337     /**
23338      * @event click
23339      * Fires when a template node is clicked.
23340      * @param {Roo.View} this
23341      * @param {Number} index The index of the target node
23342      * @param {HTMLElement} node The target node
23343      * @param {Roo.EventObject} e The raw event object
23344      */
23345         "click" : true,
23346     /**
23347      * @event dblclick
23348      * Fires when a template node is double clicked.
23349      * @param {Roo.View} this
23350      * @param {Number} index The index of the target node
23351      * @param {HTMLElement} node The target node
23352      * @param {Roo.EventObject} e The raw event object
23353      */
23354         "dblclick" : true,
23355     /**
23356      * @event contextmenu
23357      * Fires when a template node is right clicked.
23358      * @param {Roo.View} this
23359      * @param {Number} index The index of the target node
23360      * @param {HTMLElement} node The target node
23361      * @param {Roo.EventObject} e The raw event object
23362      */
23363         "contextmenu" : true,
23364     /**
23365      * @event selectionchange
23366      * Fires when the selected nodes change.
23367      * @param {Roo.View} this
23368      * @param {Array} selections Array of the selected nodes
23369      */
23370         "selectionchange" : true,
23371
23372     /**
23373      * @event beforeselect
23374      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23375      * @param {Roo.View} this
23376      * @param {HTMLElement} node The node to be selected
23377      * @param {Array} selections Array of currently selected nodes
23378      */
23379         "beforeselect" : true
23380     });
23381
23382     this.el.on({
23383         "click": this.onClick,
23384         "dblclick": this.onDblClick,
23385         "contextmenu": this.onContextMenu,
23386         scope:this
23387     });
23388
23389     this.selections = [];
23390     this.nodes = [];
23391     this.cmp = new Roo.CompositeElementLite([]);
23392     if(this.store){
23393         this.store = Roo.factory(this.store, Roo.data);
23394         this.setStore(this.store, true);
23395     }
23396     Roo.View.superclass.constructor.call(this);
23397 };
23398
23399 Roo.extend(Roo.View, Roo.util.Observable, {
23400     
23401      /**
23402      * @cfg {Roo.data.Store} store Data store to load data from.
23403      */
23404     store : false,
23405     
23406     /**
23407      * @cfg {String|Roo.Element} el The container element.
23408      */
23409     el : '',
23410     
23411     /**
23412      * @cfg {String|Roo.Template} tpl The template used by this View 
23413      */
23414     tpl : false,
23415     
23416     /**
23417      * @cfg {String} selectedClass The css class to add to selected nodes
23418      */
23419     selectedClass : "x-view-selected",
23420      /**
23421      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23422      */
23423     emptyText : "",
23424     /**
23425      * Returns the element this view is bound to.
23426      * @return {Roo.Element}
23427      */
23428     getEl : function(){
23429         return this.el;
23430     },
23431
23432     /**
23433      * Refreshes the view.
23434      */
23435     refresh : function(){
23436         var t = this.tpl;
23437         this.clearSelections();
23438         this.el.update("");
23439         var html = [];
23440         var records = this.store.getRange();
23441         if(records.length < 1){
23442             this.el.update(this.emptyText);
23443             return;
23444         }
23445         for(var i = 0, len = records.length; i < len; i++){
23446             var data = this.prepareData(records[i].data, i, records[i]);
23447             html[html.length] = t.apply(data);
23448         }
23449         this.el.update(html.join(""));
23450         this.nodes = this.el.dom.childNodes;
23451         this.updateIndexes(0);
23452     },
23453
23454     /**
23455      * Function to override to reformat the data that is sent to
23456      * the template for each node.
23457      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23458      * a JSON object for an UpdateManager bound view).
23459      */
23460     prepareData : function(data){
23461         return data;
23462     },
23463
23464     onUpdate : function(ds, record){
23465         this.clearSelections();
23466         var index = this.store.indexOf(record);
23467         var n = this.nodes[index];
23468         this.tpl.insertBefore(n, this.prepareData(record.data));
23469         n.parentNode.removeChild(n);
23470         this.updateIndexes(index, index);
23471     },
23472
23473     onAdd : function(ds, records, index){
23474         this.clearSelections();
23475         if(this.nodes.length == 0){
23476             this.refresh();
23477             return;
23478         }
23479         var n = this.nodes[index];
23480         for(var i = 0, len = records.length; i < len; i++){
23481             var d = this.prepareData(records[i].data);
23482             if(n){
23483                 this.tpl.insertBefore(n, d);
23484             }else{
23485                 this.tpl.append(this.el, d);
23486             }
23487         }
23488         this.updateIndexes(index);
23489     },
23490
23491     onRemove : function(ds, record, index){
23492         this.clearSelections();
23493         this.el.dom.removeChild(this.nodes[index]);
23494         this.updateIndexes(index);
23495     },
23496
23497     /**
23498      * Refresh an individual node.
23499      * @param {Number} index
23500      */
23501     refreshNode : function(index){
23502         this.onUpdate(this.store, this.store.getAt(index));
23503     },
23504
23505     updateIndexes : function(startIndex, endIndex){
23506         var ns = this.nodes;
23507         startIndex = startIndex || 0;
23508         endIndex = endIndex || ns.length - 1;
23509         for(var i = startIndex; i <= endIndex; i++){
23510             ns[i].nodeIndex = i;
23511         }
23512     },
23513
23514     /**
23515      * Changes the data store this view uses and refresh the view.
23516      * @param {Store} store
23517      */
23518     setStore : function(store, initial){
23519         if(!initial && this.store){
23520             this.store.un("datachanged", this.refresh);
23521             this.store.un("add", this.onAdd);
23522             this.store.un("remove", this.onRemove);
23523             this.store.un("update", this.onUpdate);
23524             this.store.un("clear", this.refresh);
23525         }
23526         if(store){
23527           
23528             store.on("datachanged", this.refresh, this);
23529             store.on("add", this.onAdd, this);
23530             store.on("remove", this.onRemove, this);
23531             store.on("update", this.onUpdate, this);
23532             store.on("clear", this.refresh, this);
23533         }
23534         
23535         if(store){
23536             this.refresh();
23537         }
23538     },
23539
23540     /**
23541      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23542      * @param {HTMLElement} node
23543      * @return {HTMLElement} The template node
23544      */
23545     findItemFromChild : function(node){
23546         var el = this.el.dom;
23547         if(!node || node.parentNode == el){
23548                     return node;
23549             }
23550             var p = node.parentNode;
23551             while(p && p != el){
23552             if(p.parentNode == el){
23553                 return p;
23554             }
23555             p = p.parentNode;
23556         }
23557             return null;
23558     },
23559
23560     /** @ignore */
23561     onClick : function(e){
23562         var item = this.findItemFromChild(e.getTarget());
23563         if(item){
23564             var index = this.indexOf(item);
23565             if(this.onItemClick(item, index, e) !== false){
23566                 this.fireEvent("click", this, index, item, e);
23567             }
23568         }else{
23569             this.clearSelections();
23570         }
23571     },
23572
23573     /** @ignore */
23574     onContextMenu : function(e){
23575         var item = this.findItemFromChild(e.getTarget());
23576         if(item){
23577             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23578         }
23579     },
23580
23581     /** @ignore */
23582     onDblClick : function(e){
23583         var item = this.findItemFromChild(e.getTarget());
23584         if(item){
23585             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23586         }
23587     },
23588
23589     onItemClick : function(item, index, e){
23590         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23591             return false;
23592         }
23593         if(this.multiSelect || this.singleSelect){
23594             if(this.multiSelect && e.shiftKey && this.lastSelection){
23595                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23596             }else{
23597                 this.select(item, this.multiSelect && e.ctrlKey);
23598                 this.lastSelection = item;
23599             }
23600             e.preventDefault();
23601         }
23602         return true;
23603     },
23604
23605     /**
23606      * Get the number of selected nodes.
23607      * @return {Number}
23608      */
23609     getSelectionCount : function(){
23610         return this.selections.length;
23611     },
23612
23613     /**
23614      * Get the currently selected nodes.
23615      * @return {Array} An array of HTMLElements
23616      */
23617     getSelectedNodes : function(){
23618         return this.selections;
23619     },
23620
23621     /**
23622      * Get the indexes of the selected nodes.
23623      * @return {Array}
23624      */
23625     getSelectedIndexes : function(){
23626         var indexes = [], s = this.selections;
23627         for(var i = 0, len = s.length; i < len; i++){
23628             indexes.push(s[i].nodeIndex);
23629         }
23630         return indexes;
23631     },
23632
23633     /**
23634      * Clear all selections
23635      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23636      */
23637     clearSelections : function(suppressEvent){
23638         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23639             this.cmp.elements = this.selections;
23640             this.cmp.removeClass(this.selectedClass);
23641             this.selections = [];
23642             if(!suppressEvent){
23643                 this.fireEvent("selectionchange", this, this.selections);
23644             }
23645         }
23646     },
23647
23648     /**
23649      * Returns true if the passed node is selected
23650      * @param {HTMLElement/Number} node The node or node index
23651      * @return {Boolean}
23652      */
23653     isSelected : function(node){
23654         var s = this.selections;
23655         if(s.length < 1){
23656             return false;
23657         }
23658         node = this.getNode(node);
23659         return s.indexOf(node) !== -1;
23660     },
23661
23662     /**
23663      * Selects nodes.
23664      * @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
23665      * @param {Boolean} keepExisting (optional) true to keep existing selections
23666      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23667      */
23668     select : function(nodeInfo, keepExisting, suppressEvent){
23669         if(nodeInfo instanceof Array){
23670             if(!keepExisting){
23671                 this.clearSelections(true);
23672             }
23673             for(var i = 0, len = nodeInfo.length; i < len; i++){
23674                 this.select(nodeInfo[i], true, true);
23675             }
23676         } else{
23677             var node = this.getNode(nodeInfo);
23678             if(node && !this.isSelected(node)){
23679                 if(!keepExisting){
23680                     this.clearSelections(true);
23681                 }
23682                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23683                     Roo.fly(node).addClass(this.selectedClass);
23684                     this.selections.push(node);
23685                     if(!suppressEvent){
23686                         this.fireEvent("selectionchange", this, this.selections);
23687                     }
23688                 }
23689             }
23690         }
23691     },
23692
23693     /**
23694      * Gets a template node.
23695      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23696      * @return {HTMLElement} The node or null if it wasn't found
23697      */
23698     getNode : function(nodeInfo){
23699         if(typeof nodeInfo == "string"){
23700             return document.getElementById(nodeInfo);
23701         }else if(typeof nodeInfo == "number"){
23702             return this.nodes[nodeInfo];
23703         }
23704         return nodeInfo;
23705     },
23706
23707     /**
23708      * Gets a range template nodes.
23709      * @param {Number} startIndex
23710      * @param {Number} endIndex
23711      * @return {Array} An array of nodes
23712      */
23713     getNodes : function(start, end){
23714         var ns = this.nodes;
23715         start = start || 0;
23716         end = typeof end == "undefined" ? ns.length - 1 : end;
23717         var nodes = [];
23718         if(start <= end){
23719             for(var i = start; i <= end; i++){
23720                 nodes.push(ns[i]);
23721             }
23722         } else{
23723             for(var i = start; i >= end; i--){
23724                 nodes.push(ns[i]);
23725             }
23726         }
23727         return nodes;
23728     },
23729
23730     /**
23731      * Finds the index of the passed node
23732      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23733      * @return {Number} The index of the node or -1
23734      */
23735     indexOf : function(node){
23736         node = this.getNode(node);
23737         if(typeof node.nodeIndex == "number"){
23738             return node.nodeIndex;
23739         }
23740         var ns = this.nodes;
23741         for(var i = 0, len = ns.length; i < len; i++){
23742             if(ns[i] == node){
23743                 return i;
23744             }
23745         }
23746         return -1;
23747     }
23748 });
23749 /*
23750  * Based on:
23751  * Ext JS Library 1.1.1
23752  * Copyright(c) 2006-2007, Ext JS, LLC.
23753  *
23754  * Originally Released Under LGPL - original licence link has changed is not relivant.
23755  *
23756  * Fork - LGPL
23757  * <script type="text/javascript">
23758  */
23759
23760 /**
23761  * @class Roo.JsonView
23762  * @extends Roo.View
23763  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23764 <pre><code>
23765 var view = new Roo.JsonView({
23766     container: "my-element",
23767     template: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23768     multiSelect: true, 
23769     jsonRoot: "data" 
23770 });
23771
23772 // listen for node click?
23773 view.on("click", function(vw, index, node, e){
23774     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23775 });
23776
23777 // direct load of JSON data
23778 view.load("foobar.php");
23779
23780 // Example from my blog list
23781 var tpl = new Roo.Template(
23782     '&lt;div class="entry"&gt;' +
23783     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23784     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23785     "&lt;/div&gt;&lt;hr /&gt;"
23786 );
23787
23788 var moreView = new Roo.JsonView({
23789     container :  "entry-list", 
23790     template : tpl,
23791     jsonRoot: "posts"
23792 });
23793 moreView.on("beforerender", this.sortEntries, this);
23794 moreView.load({
23795     url: "/blog/get-posts.php",
23796     params: "allposts=true",
23797     text: "Loading Blog Entries..."
23798 });
23799 </code></pre>
23800
23801 * Note: old code is supported with arguments : (container, template, config)
23802
23803
23804  * @constructor
23805  * Create a new JsonView
23806  * 
23807  * @param {Object} config The config object
23808  * 
23809  */
23810 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23811     
23812     
23813     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23814
23815     var um = this.el.getUpdateManager();
23816     um.setRenderer(this);
23817     um.on("update", this.onLoad, this);
23818     um.on("failure", this.onLoadException, this);
23819
23820     /**
23821      * @event beforerender
23822      * Fires before rendering of the downloaded JSON data.
23823      * @param {Roo.JsonView} this
23824      * @param {Object} data The JSON data loaded
23825      */
23826     /**
23827      * @event load
23828      * Fires when data is loaded.
23829      * @param {Roo.JsonView} this
23830      * @param {Object} data The JSON data loaded
23831      * @param {Object} response The raw Connect response object
23832      */
23833     /**
23834      * @event loadexception
23835      * Fires when loading fails.
23836      * @param {Roo.JsonView} this
23837      * @param {Object} response The raw Connect response object
23838      */
23839     this.addEvents({
23840         'beforerender' : true,
23841         'load' : true,
23842         'loadexception' : true
23843     });
23844 };
23845 Roo.extend(Roo.JsonView, Roo.View, {
23846     /**
23847      * @type {String} The root property in the loaded JSON object that contains the data
23848      */
23849     jsonRoot : "",
23850
23851     /**
23852      * Refreshes the view.
23853      */
23854     refresh : function(){
23855         this.clearSelections();
23856         this.el.update("");
23857         var html = [];
23858         var o = this.jsonData;
23859         if(o && o.length > 0){
23860             for(var i = 0, len = o.length; i < len; i++){
23861                 var data = this.prepareData(o[i], i, o);
23862                 html[html.length] = this.tpl.apply(data);
23863             }
23864         }else{
23865             html.push(this.emptyText);
23866         }
23867         this.el.update(html.join(""));
23868         this.nodes = this.el.dom.childNodes;
23869         this.updateIndexes(0);
23870     },
23871
23872     /**
23873      * 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.
23874      * @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:
23875      <pre><code>
23876      view.load({
23877          url: "your-url.php",
23878          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23879          callback: yourFunction,
23880          scope: yourObject, //(optional scope)
23881          discardUrl: false,
23882          nocache: false,
23883          text: "Loading...",
23884          timeout: 30,
23885          scripts: false
23886      });
23887      </code></pre>
23888      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23889      * 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.
23890      * @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}
23891      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23892      * @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.
23893      */
23894     load : function(){
23895         var um = this.el.getUpdateManager();
23896         um.update.apply(um, arguments);
23897     },
23898
23899     render : function(el, response){
23900         this.clearSelections();
23901         this.el.update("");
23902         var o;
23903         try{
23904             o = Roo.util.JSON.decode(response.responseText);
23905             if(this.jsonRoot){
23906                 
23907                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
23908             }
23909         } catch(e){
23910         }
23911         /**
23912          * The current JSON data or null
23913          */
23914         this.jsonData = o;
23915         this.beforeRender();
23916         this.refresh();
23917     },
23918
23919 /**
23920  * Get the number of records in the current JSON dataset
23921  * @return {Number}
23922  */
23923     getCount : function(){
23924         return this.jsonData ? this.jsonData.length : 0;
23925     },
23926
23927 /**
23928  * Returns the JSON object for the specified node(s)
23929  * @param {HTMLElement/Array} node The node or an array of nodes
23930  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23931  * you get the JSON object for the node
23932  */
23933     getNodeData : function(node){
23934         if(node instanceof Array){
23935             var data = [];
23936             for(var i = 0, len = node.length; i < len; i++){
23937                 data.push(this.getNodeData(node[i]));
23938             }
23939             return data;
23940         }
23941         return this.jsonData[this.indexOf(node)] || null;
23942     },
23943
23944     beforeRender : function(){
23945         this.snapshot = this.jsonData;
23946         if(this.sortInfo){
23947             this.sort.apply(this, this.sortInfo);
23948         }
23949         this.fireEvent("beforerender", this, this.jsonData);
23950     },
23951
23952     onLoad : function(el, o){
23953         this.fireEvent("load", this, this.jsonData, o);
23954     },
23955
23956     onLoadException : function(el, o){
23957         this.fireEvent("loadexception", this, o);
23958     },
23959
23960 /**
23961  * Filter the data by a specific property.
23962  * @param {String} property A property on your JSON objects
23963  * @param {String/RegExp} value Either string that the property values
23964  * should start with, or a RegExp to test against the property
23965  */
23966     filter : function(property, value){
23967         if(this.jsonData){
23968             var data = [];
23969             var ss = this.snapshot;
23970             if(typeof value == "string"){
23971                 var vlen = value.length;
23972                 if(vlen == 0){
23973                     this.clearFilter();
23974                     return;
23975                 }
23976                 value = value.toLowerCase();
23977                 for(var i = 0, len = ss.length; i < len; i++){
23978                     var o = ss[i];
23979                     if(o[property].substr(0, vlen).toLowerCase() == value){
23980                         data.push(o);
23981                     }
23982                 }
23983             } else if(value.exec){ // regex?
23984                 for(var i = 0, len = ss.length; i < len; i++){
23985                     var o = ss[i];
23986                     if(value.test(o[property])){
23987                         data.push(o);
23988                     }
23989                 }
23990             } else{
23991                 return;
23992             }
23993             this.jsonData = data;
23994             this.refresh();
23995         }
23996     },
23997
23998 /**
23999  * Filter by a function. The passed function will be called with each
24000  * object in the current dataset. If the function returns true the value is kept,
24001  * otherwise it is filtered.
24002  * @param {Function} fn
24003  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24004  */
24005     filterBy : function(fn, scope){
24006         if(this.jsonData){
24007             var data = [];
24008             var ss = this.snapshot;
24009             for(var i = 0, len = ss.length; i < len; i++){
24010                 var o = ss[i];
24011                 if(fn.call(scope || this, o)){
24012                     data.push(o);
24013                 }
24014             }
24015             this.jsonData = data;
24016             this.refresh();
24017         }
24018     },
24019
24020 /**
24021  * Clears the current filter.
24022  */
24023     clearFilter : function(){
24024         if(this.snapshot && this.jsonData != this.snapshot){
24025             this.jsonData = this.snapshot;
24026             this.refresh();
24027         }
24028     },
24029
24030
24031 /**
24032  * Sorts the data for this view and refreshes it.
24033  * @param {String} property A property on your JSON objects to sort on
24034  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24035  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24036  */
24037     sort : function(property, dir, sortType){
24038         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24039         if(this.jsonData){
24040             var p = property;
24041             var dsc = dir && dir.toLowerCase() == "desc";
24042             var f = function(o1, o2){
24043                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24044                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24045                 ;
24046                 if(v1 < v2){
24047                     return dsc ? +1 : -1;
24048                 } else if(v1 > v2){
24049                     return dsc ? -1 : +1;
24050                 } else{
24051                     return 0;
24052                 }
24053             };
24054             this.jsonData.sort(f);
24055             this.refresh();
24056             if(this.jsonData != this.snapshot){
24057                 this.snapshot.sort(f);
24058             }
24059         }
24060     }
24061 });/*
24062  * Based on:
24063  * Ext JS Library 1.1.1
24064  * Copyright(c) 2006-2007, Ext JS, LLC.
24065  *
24066  * Originally Released Under LGPL - original licence link has changed is not relivant.
24067  *
24068  * Fork - LGPL
24069  * <script type="text/javascript">
24070  */
24071  
24072
24073 /**
24074  * @class Roo.ColorPalette
24075  * @extends Roo.Component
24076  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24077  * Here's an example of typical usage:
24078  * <pre><code>
24079 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24080 cp.render('my-div');
24081
24082 cp.on('select', function(palette, selColor){
24083     // do something with selColor
24084 });
24085 </code></pre>
24086  * @constructor
24087  * Create a new ColorPalette
24088  * @param {Object} config The config object
24089  */
24090 Roo.ColorPalette = function(config){
24091     Roo.ColorPalette.superclass.constructor.call(this, config);
24092     this.addEvents({
24093         /**
24094              * @event select
24095              * Fires when a color is selected
24096              * @param {ColorPalette} this
24097              * @param {String} color The 6-digit color hex code (without the # symbol)
24098              */
24099         select: true
24100     });
24101
24102     if(this.handler){
24103         this.on("select", this.handler, this.scope, true);
24104     }
24105 };
24106 Roo.extend(Roo.ColorPalette, Roo.Component, {
24107     /**
24108      * @cfg {String} itemCls
24109      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24110      */
24111     itemCls : "x-color-palette",
24112     /**
24113      * @cfg {String} value
24114      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24115      * the hex codes are case-sensitive.
24116      */
24117     value : null,
24118     clickEvent:'click',
24119     // private
24120     ctype: "Roo.ColorPalette",
24121
24122     /**
24123      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24124      */
24125     allowReselect : false,
24126
24127     /**
24128      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24129      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24130      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24131      * of colors with the width setting until the box is symmetrical.</p>
24132      * <p>You can override individual colors if needed:</p>
24133      * <pre><code>
24134 var cp = new Roo.ColorPalette();
24135 cp.colors[0] = "FF0000";  // change the first box to red
24136 </code></pre>
24137
24138 Or you can provide a custom array of your own for complete control:
24139 <pre><code>
24140 var cp = new Roo.ColorPalette();
24141 cp.colors = ["000000", "993300", "333300"];
24142 </code></pre>
24143      * @type Array
24144      */
24145     colors : [
24146         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24147         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24148         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24149         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24150         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24151     ],
24152
24153     // private
24154     onRender : function(container, position){
24155         var t = new Roo.MasterTemplate(
24156             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24157         );
24158         var c = this.colors;
24159         for(var i = 0, len = c.length; i < len; i++){
24160             t.add([c[i]]);
24161         }
24162         var el = document.createElement("div");
24163         el.className = this.itemCls;
24164         t.overwrite(el);
24165         container.dom.insertBefore(el, position);
24166         this.el = Roo.get(el);
24167         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24168         if(this.clickEvent != 'click'){
24169             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24170         }
24171     },
24172
24173     // private
24174     afterRender : function(){
24175         Roo.ColorPalette.superclass.afterRender.call(this);
24176         if(this.value){
24177             var s = this.value;
24178             this.value = null;
24179             this.select(s);
24180         }
24181     },
24182
24183     // private
24184     handleClick : function(e, t){
24185         e.preventDefault();
24186         if(!this.disabled){
24187             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24188             this.select(c.toUpperCase());
24189         }
24190     },
24191
24192     /**
24193      * Selects the specified color in the palette (fires the select event)
24194      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24195      */
24196     select : function(color){
24197         color = color.replace("#", "");
24198         if(color != this.value || this.allowReselect){
24199             var el = this.el;
24200             if(this.value){
24201                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24202             }
24203             el.child("a.color-"+color).addClass("x-color-palette-sel");
24204             this.value = color;
24205             this.fireEvent("select", this, color);
24206         }
24207     }
24208 });/*
24209  * Based on:
24210  * Ext JS Library 1.1.1
24211  * Copyright(c) 2006-2007, Ext JS, LLC.
24212  *
24213  * Originally Released Under LGPL - original licence link has changed is not relivant.
24214  *
24215  * Fork - LGPL
24216  * <script type="text/javascript">
24217  */
24218  
24219 /**
24220  * @class Roo.DatePicker
24221  * @extends Roo.Component
24222  * Simple date picker class.
24223  * @constructor
24224  * Create a new DatePicker
24225  * @param {Object} config The config object
24226  */
24227 Roo.DatePicker = function(config){
24228     Roo.DatePicker.superclass.constructor.call(this, config);
24229
24230     this.value = config && config.value ?
24231                  config.value.clearTime() : new Date().clearTime();
24232
24233     this.addEvents({
24234         /**
24235              * @event select
24236              * Fires when a date is selected
24237              * @param {DatePicker} this
24238              * @param {Date} date The selected date
24239              */
24240         select: true
24241     });
24242
24243     if(this.handler){
24244         this.on("select", this.handler,  this.scope || this);
24245     }
24246     // build the disabledDatesRE
24247     if(!this.disabledDatesRE && this.disabledDates){
24248         var dd = this.disabledDates;
24249         var re = "(?:";
24250         for(var i = 0; i < dd.length; i++){
24251             re += dd[i];
24252             if(i != dd.length-1) re += "|";
24253         }
24254         this.disabledDatesRE = new RegExp(re + ")");
24255     }
24256 };
24257
24258 Roo.extend(Roo.DatePicker, Roo.Component, {
24259     /**
24260      * @cfg {String} todayText
24261      * The text to display on the button that selects the current date (defaults to "Today")
24262      */
24263     todayText : "Today",
24264     /**
24265      * @cfg {String} okText
24266      * The text to display on the ok button
24267      */
24268     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24269     /**
24270      * @cfg {String} cancelText
24271      * The text to display on the cancel button
24272      */
24273     cancelText : "Cancel",
24274     /**
24275      * @cfg {String} todayTip
24276      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24277      */
24278     todayTip : "{0} (Spacebar)",
24279     /**
24280      * @cfg {Date} minDate
24281      * Minimum allowable date (JavaScript date object, defaults to null)
24282      */
24283     minDate : null,
24284     /**
24285      * @cfg {Date} maxDate
24286      * Maximum allowable date (JavaScript date object, defaults to null)
24287      */
24288     maxDate : null,
24289     /**
24290      * @cfg {String} minText
24291      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24292      */
24293     minText : "This date is before the minimum date",
24294     /**
24295      * @cfg {String} maxText
24296      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24297      */
24298     maxText : "This date is after the maximum date",
24299     /**
24300      * @cfg {String} format
24301      * The default date format string which can be overriden for localization support.  The format must be
24302      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24303      */
24304     format : "m/d/y",
24305     /**
24306      * @cfg {Array} disabledDays
24307      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24308      */
24309     disabledDays : null,
24310     /**
24311      * @cfg {String} disabledDaysText
24312      * The tooltip to display when the date falls on a disabled day (defaults to "")
24313      */
24314     disabledDaysText : "",
24315     /**
24316      * @cfg {RegExp} disabledDatesRE
24317      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24318      */
24319     disabledDatesRE : null,
24320     /**
24321      * @cfg {String} disabledDatesText
24322      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24323      */
24324     disabledDatesText : "",
24325     /**
24326      * @cfg {Boolean} constrainToViewport
24327      * True to constrain the date picker to the viewport (defaults to true)
24328      */
24329     constrainToViewport : true,
24330     /**
24331      * @cfg {Array} monthNames
24332      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24333      */
24334     monthNames : Date.monthNames,
24335     /**
24336      * @cfg {Array} dayNames
24337      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24338      */
24339     dayNames : Date.dayNames,
24340     /**
24341      * @cfg {String} nextText
24342      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24343      */
24344     nextText: 'Next Month (Control+Right)',
24345     /**
24346      * @cfg {String} prevText
24347      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24348      */
24349     prevText: 'Previous Month (Control+Left)',
24350     /**
24351      * @cfg {String} monthYearText
24352      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24353      */
24354     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24355     /**
24356      * @cfg {Number} startDay
24357      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24358      */
24359     startDay : 0,
24360     /**
24361      * @cfg {Bool} showClear
24362      * Show a clear button (usefull for date form elements that can be blank.)
24363      */
24364     
24365     showClear: false,
24366     
24367     /**
24368      * Sets the value of the date field
24369      * @param {Date} value The date to set
24370      */
24371     setValue : function(value){
24372         var old = this.value;
24373         this.value = value.clearTime(true);
24374         if(this.el){
24375             this.update(this.value);
24376         }
24377     },
24378
24379     /**
24380      * Gets the current selected value of the date field
24381      * @return {Date} The selected date
24382      */
24383     getValue : function(){
24384         return this.value;
24385     },
24386
24387     // private
24388     focus : function(){
24389         if(this.el){
24390             this.update(this.activeDate);
24391         }
24392     },
24393
24394     // private
24395     onRender : function(container, position){
24396         var m = [
24397              '<table cellspacing="0">',
24398                 '<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>',
24399                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24400         var dn = this.dayNames;
24401         for(var i = 0; i < 7; i++){
24402             var d = this.startDay+i;
24403             if(d > 6){
24404                 d = d-7;
24405             }
24406             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24407         }
24408         m[m.length] = "</tr></thead><tbody><tr>";
24409         for(var i = 0; i < 42; i++) {
24410             if(i % 7 == 0 && i != 0){
24411                 m[m.length] = "</tr><tr>";
24412             }
24413             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24414         }
24415         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24416             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24417
24418         var el = document.createElement("div");
24419         el.className = "x-date-picker";
24420         el.innerHTML = m.join("");
24421
24422         container.dom.insertBefore(el, position);
24423
24424         this.el = Roo.get(el);
24425         this.eventEl = Roo.get(el.firstChild);
24426
24427         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24428             handler: this.showPrevMonth,
24429             scope: this,
24430             preventDefault:true,
24431             stopDefault:true
24432         });
24433
24434         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24435             handler: this.showNextMonth,
24436             scope: this,
24437             preventDefault:true,
24438             stopDefault:true
24439         });
24440
24441         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24442
24443         this.monthPicker = this.el.down('div.x-date-mp');
24444         this.monthPicker.enableDisplayMode('block');
24445         
24446         var kn = new Roo.KeyNav(this.eventEl, {
24447             "left" : function(e){
24448                 e.ctrlKey ?
24449                     this.showPrevMonth() :
24450                     this.update(this.activeDate.add("d", -1));
24451             },
24452
24453             "right" : function(e){
24454                 e.ctrlKey ?
24455                     this.showNextMonth() :
24456                     this.update(this.activeDate.add("d", 1));
24457             },
24458
24459             "up" : function(e){
24460                 e.ctrlKey ?
24461                     this.showNextYear() :
24462                     this.update(this.activeDate.add("d", -7));
24463             },
24464
24465             "down" : function(e){
24466                 e.ctrlKey ?
24467                     this.showPrevYear() :
24468                     this.update(this.activeDate.add("d", 7));
24469             },
24470
24471             "pageUp" : function(e){
24472                 this.showNextMonth();
24473             },
24474
24475             "pageDown" : function(e){
24476                 this.showPrevMonth();
24477             },
24478
24479             "enter" : function(e){
24480                 e.stopPropagation();
24481                 return true;
24482             },
24483
24484             scope : this
24485         });
24486
24487         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24488
24489         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24490
24491         this.el.unselectable();
24492         
24493         this.cells = this.el.select("table.x-date-inner tbody td");
24494         this.textNodes = this.el.query("table.x-date-inner tbody span");
24495
24496         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24497             text: "&#160;",
24498             tooltip: this.monthYearText
24499         });
24500
24501         this.mbtn.on('click', this.showMonthPicker, this);
24502         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24503
24504
24505         var today = (new Date()).dateFormat(this.format);
24506         
24507         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24508         baseTb.add({
24509             text: String.format(this.todayText, today),
24510             tooltip: String.format(this.todayTip, today),
24511             handler: this.selectToday,
24512             scope: this
24513         });
24514         
24515         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24516             
24517         //});
24518         if (this.showClear) {
24519             
24520             baseTb.add( new Roo.Toolbar.Fill());
24521             baseTb.add({
24522                 text: '&#160;',
24523                 cls: 'x-btn-icon x-btn-clear',
24524                 handler: function() {
24525                     //this.value = '';
24526                     this.fireEvent("select", this, '');
24527                 },
24528                 scope: this
24529             });
24530         }
24531         
24532         
24533         if(Roo.isIE){
24534             this.el.repaint();
24535         }
24536         this.update(this.value);
24537     },
24538
24539     createMonthPicker : function(){
24540         if(!this.monthPicker.dom.firstChild){
24541             var buf = ['<table border="0" cellspacing="0">'];
24542             for(var i = 0; i < 6; i++){
24543                 buf.push(
24544                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24545                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24546                     i == 0 ?
24547                     '<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>' :
24548                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24549                 );
24550             }
24551             buf.push(
24552                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24553                     this.okText,
24554                     '</button><button type="button" class="x-date-mp-cancel">',
24555                     this.cancelText,
24556                     '</button></td></tr>',
24557                 '</table>'
24558             );
24559             this.monthPicker.update(buf.join(''));
24560             this.monthPicker.on('click', this.onMonthClick, this);
24561             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24562
24563             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24564             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24565
24566             this.mpMonths.each(function(m, a, i){
24567                 i += 1;
24568                 if((i%2) == 0){
24569                     m.dom.xmonth = 5 + Math.round(i * .5);
24570                 }else{
24571                     m.dom.xmonth = Math.round((i-1) * .5);
24572                 }
24573             });
24574         }
24575     },
24576
24577     showMonthPicker : function(){
24578         this.createMonthPicker();
24579         var size = this.el.getSize();
24580         this.monthPicker.setSize(size);
24581         this.monthPicker.child('table').setSize(size);
24582
24583         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24584         this.updateMPMonth(this.mpSelMonth);
24585         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24586         this.updateMPYear(this.mpSelYear);
24587
24588         this.monthPicker.slideIn('t', {duration:.2});
24589     },
24590
24591     updateMPYear : function(y){
24592         this.mpyear = y;
24593         var ys = this.mpYears.elements;
24594         for(var i = 1; i <= 10; i++){
24595             var td = ys[i-1], y2;
24596             if((i%2) == 0){
24597                 y2 = y + Math.round(i * .5);
24598                 td.firstChild.innerHTML = y2;
24599                 td.xyear = y2;
24600             }else{
24601                 y2 = y - (5-Math.round(i * .5));
24602                 td.firstChild.innerHTML = y2;
24603                 td.xyear = y2;
24604             }
24605             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24606         }
24607     },
24608
24609     updateMPMonth : function(sm){
24610         this.mpMonths.each(function(m, a, i){
24611             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24612         });
24613     },
24614
24615     selectMPMonth: function(m){
24616         
24617     },
24618
24619     onMonthClick : function(e, t){
24620         e.stopEvent();
24621         var el = new Roo.Element(t), pn;
24622         if(el.is('button.x-date-mp-cancel')){
24623             this.hideMonthPicker();
24624         }
24625         else if(el.is('button.x-date-mp-ok')){
24626             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24627             this.hideMonthPicker();
24628         }
24629         else if(pn = el.up('td.x-date-mp-month', 2)){
24630             this.mpMonths.removeClass('x-date-mp-sel');
24631             pn.addClass('x-date-mp-sel');
24632             this.mpSelMonth = pn.dom.xmonth;
24633         }
24634         else if(pn = el.up('td.x-date-mp-year', 2)){
24635             this.mpYears.removeClass('x-date-mp-sel');
24636             pn.addClass('x-date-mp-sel');
24637             this.mpSelYear = pn.dom.xyear;
24638         }
24639         else if(el.is('a.x-date-mp-prev')){
24640             this.updateMPYear(this.mpyear-10);
24641         }
24642         else if(el.is('a.x-date-mp-next')){
24643             this.updateMPYear(this.mpyear+10);
24644         }
24645     },
24646
24647     onMonthDblClick : function(e, t){
24648         e.stopEvent();
24649         var el = new Roo.Element(t), pn;
24650         if(pn = el.up('td.x-date-mp-month', 2)){
24651             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24652             this.hideMonthPicker();
24653         }
24654         else if(pn = el.up('td.x-date-mp-year', 2)){
24655             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24656             this.hideMonthPicker();
24657         }
24658     },
24659
24660     hideMonthPicker : function(disableAnim){
24661         if(this.monthPicker){
24662             if(disableAnim === true){
24663                 this.monthPicker.hide();
24664             }else{
24665                 this.monthPicker.slideOut('t', {duration:.2});
24666             }
24667         }
24668     },
24669
24670     // private
24671     showPrevMonth : function(e){
24672         this.update(this.activeDate.add("mo", -1));
24673     },
24674
24675     // private
24676     showNextMonth : function(e){
24677         this.update(this.activeDate.add("mo", 1));
24678     },
24679
24680     // private
24681     showPrevYear : function(){
24682         this.update(this.activeDate.add("y", -1));
24683     },
24684
24685     // private
24686     showNextYear : function(){
24687         this.update(this.activeDate.add("y", 1));
24688     },
24689
24690     // private
24691     handleMouseWheel : function(e){
24692         var delta = e.getWheelDelta();
24693         if(delta > 0){
24694             this.showPrevMonth();
24695             e.stopEvent();
24696         } else if(delta < 0){
24697             this.showNextMonth();
24698             e.stopEvent();
24699         }
24700     },
24701
24702     // private
24703     handleDateClick : function(e, t){
24704         e.stopEvent();
24705         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24706             this.setValue(new Date(t.dateValue));
24707             this.fireEvent("select", this, this.value);
24708         }
24709     },
24710
24711     // private
24712     selectToday : function(){
24713         this.setValue(new Date().clearTime());
24714         this.fireEvent("select", this, this.value);
24715     },
24716
24717     // private
24718     update : function(date){
24719         var vd = this.activeDate;
24720         this.activeDate = date;
24721         if(vd && this.el){
24722             var t = date.getTime();
24723             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24724                 this.cells.removeClass("x-date-selected");
24725                 this.cells.each(function(c){
24726                    if(c.dom.firstChild.dateValue == t){
24727                        c.addClass("x-date-selected");
24728                        setTimeout(function(){
24729                             try{c.dom.firstChild.focus();}catch(e){}
24730                        }, 50);
24731                        return false;
24732                    }
24733                 });
24734                 return;
24735             }
24736         }
24737         var days = date.getDaysInMonth();
24738         var firstOfMonth = date.getFirstDateOfMonth();
24739         var startingPos = firstOfMonth.getDay()-this.startDay;
24740
24741         if(startingPos <= this.startDay){
24742             startingPos += 7;
24743         }
24744
24745         var pm = date.add("mo", -1);
24746         var prevStart = pm.getDaysInMonth()-startingPos;
24747
24748         var cells = this.cells.elements;
24749         var textEls = this.textNodes;
24750         days += startingPos;
24751
24752         // convert everything to numbers so it's fast
24753         var day = 86400000;
24754         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24755         var today = new Date().clearTime().getTime();
24756         var sel = date.clearTime().getTime();
24757         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24758         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24759         var ddMatch = this.disabledDatesRE;
24760         var ddText = this.disabledDatesText;
24761         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24762         var ddaysText = this.disabledDaysText;
24763         var format = this.format;
24764
24765         var setCellClass = function(cal, cell){
24766             cell.title = "";
24767             var t = d.getTime();
24768             cell.firstChild.dateValue = t;
24769             if(t == today){
24770                 cell.className += " x-date-today";
24771                 cell.title = cal.todayText;
24772             }
24773             if(t == sel){
24774                 cell.className += " x-date-selected";
24775                 setTimeout(function(){
24776                     try{cell.firstChild.focus();}catch(e){}
24777                 }, 50);
24778             }
24779             // disabling
24780             if(t < min) {
24781                 cell.className = " x-date-disabled";
24782                 cell.title = cal.minText;
24783                 return;
24784             }
24785             if(t > max) {
24786                 cell.className = " x-date-disabled";
24787                 cell.title = cal.maxText;
24788                 return;
24789             }
24790             if(ddays){
24791                 if(ddays.indexOf(d.getDay()) != -1){
24792                     cell.title = ddaysText;
24793                     cell.className = " x-date-disabled";
24794                 }
24795             }
24796             if(ddMatch && format){
24797                 var fvalue = d.dateFormat(format);
24798                 if(ddMatch.test(fvalue)){
24799                     cell.title = ddText.replace("%0", fvalue);
24800                     cell.className = " x-date-disabled";
24801                 }
24802             }
24803         };
24804
24805         var i = 0;
24806         for(; i < startingPos; i++) {
24807             textEls[i].innerHTML = (++prevStart);
24808             d.setDate(d.getDate()+1);
24809             cells[i].className = "x-date-prevday";
24810             setCellClass(this, cells[i]);
24811         }
24812         for(; i < days; i++){
24813             intDay = i - startingPos + 1;
24814             textEls[i].innerHTML = (intDay);
24815             d.setDate(d.getDate()+1);
24816             cells[i].className = "x-date-active";
24817             setCellClass(this, cells[i]);
24818         }
24819         var extraDays = 0;
24820         for(; i < 42; i++) {
24821              textEls[i].innerHTML = (++extraDays);
24822              d.setDate(d.getDate()+1);
24823              cells[i].className = "x-date-nextday";
24824              setCellClass(this, cells[i]);
24825         }
24826
24827         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24828
24829         if(!this.internalRender){
24830             var main = this.el.dom.firstChild;
24831             var w = main.offsetWidth;
24832             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24833             Roo.fly(main).setWidth(w);
24834             this.internalRender = true;
24835             // opera does not respect the auto grow header center column
24836             // then, after it gets a width opera refuses to recalculate
24837             // without a second pass
24838             if(Roo.isOpera && !this.secondPass){
24839                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24840                 this.secondPass = true;
24841                 this.update.defer(10, this, [date]);
24842             }
24843         }
24844     }
24845 });/*
24846  * Based on:
24847  * Ext JS Library 1.1.1
24848  * Copyright(c) 2006-2007, Ext JS, LLC.
24849  *
24850  * Originally Released Under LGPL - original licence link has changed is not relivant.
24851  *
24852  * Fork - LGPL
24853  * <script type="text/javascript">
24854  */
24855 /**
24856  * @class Roo.TabPanel
24857  * @extends Roo.util.Observable
24858  * A lightweight tab container.
24859  * <br><br>
24860  * Usage:
24861  * <pre><code>
24862 // basic tabs 1, built from existing content
24863 var tabs = new Roo.TabPanel("tabs1");
24864 tabs.addTab("script", "View Script");
24865 tabs.addTab("markup", "View Markup");
24866 tabs.activate("script");
24867
24868 // more advanced tabs, built from javascript
24869 var jtabs = new Roo.TabPanel("jtabs");
24870 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24871
24872 // set up the UpdateManager
24873 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24874 var updater = tab2.getUpdateManager();
24875 updater.setDefaultUrl("ajax1.htm");
24876 tab2.on('activate', updater.refresh, updater, true);
24877
24878 // Use setUrl for Ajax loading
24879 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24880 tab3.setUrl("ajax2.htm", null, true);
24881
24882 // Disabled tab
24883 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24884 tab4.disable();
24885
24886 jtabs.activate("jtabs-1");
24887  * </code></pre>
24888  * @constructor
24889  * Create a new TabPanel.
24890  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24891  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24892  */
24893 Roo.TabPanel = function(container, config){
24894     /**
24895     * The container element for this TabPanel.
24896     * @type Roo.Element
24897     */
24898     this.el = Roo.get(container, true);
24899     if(config){
24900         if(typeof config == "boolean"){
24901             this.tabPosition = config ? "bottom" : "top";
24902         }else{
24903             Roo.apply(this, config);
24904         }
24905     }
24906     if(this.tabPosition == "bottom"){
24907         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24908         this.el.addClass("x-tabs-bottom");
24909     }
24910     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24911     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24912     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24913     if(Roo.isIE){
24914         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24915     }
24916     if(this.tabPosition != "bottom"){
24917     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24918      * @type Roo.Element
24919      */
24920       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24921       this.el.addClass("x-tabs-top");
24922     }
24923     this.items = [];
24924
24925     this.bodyEl.setStyle("position", "relative");
24926
24927     this.active = null;
24928     this.activateDelegate = this.activate.createDelegate(this);
24929
24930     this.addEvents({
24931         /**
24932          * @event tabchange
24933          * Fires when the active tab changes
24934          * @param {Roo.TabPanel} this
24935          * @param {Roo.TabPanelItem} activePanel The new active tab
24936          */
24937         "tabchange": true,
24938         /**
24939          * @event beforetabchange
24940          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24941          * @param {Roo.TabPanel} this
24942          * @param {Object} e Set cancel to true on this object to cancel the tab change
24943          * @param {Roo.TabPanelItem} tab The tab being changed to
24944          */
24945         "beforetabchange" : true
24946     });
24947
24948     Roo.EventManager.onWindowResize(this.onResize, this);
24949     this.cpad = this.el.getPadding("lr");
24950     this.hiddenCount = 0;
24951
24952     Roo.TabPanel.superclass.constructor.call(this);
24953 };
24954
24955 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
24956         /*
24957          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
24958          */
24959     tabPosition : "top",
24960         /*
24961          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
24962          */
24963     currentTabWidth : 0,
24964         /*
24965          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
24966          */
24967     minTabWidth : 40,
24968         /*
24969          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
24970          */
24971     maxTabWidth : 250,
24972         /*
24973          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
24974          */
24975     preferredTabWidth : 175,
24976         /*
24977          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
24978          */
24979     resizeTabs : false,
24980         /*
24981          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
24982          */
24983     monitorResize : true,
24984
24985     /**
24986      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
24987      * @param {String} id The id of the div to use <b>or create</b>
24988      * @param {String} text The text for the tab
24989      * @param {String} content (optional) Content to put in the TabPanelItem body
24990      * @param {Boolean} closable (optional) True to create a close icon on the tab
24991      * @return {Roo.TabPanelItem} The created TabPanelItem
24992      */
24993     addTab : function(id, text, content, closable){
24994         var item = new Roo.TabPanelItem(this, id, text, closable);
24995         this.addTabItem(item);
24996         if(content){
24997             item.setContent(content);
24998         }
24999         return item;
25000     },
25001
25002     /**
25003      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25004      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25005      * @return {Roo.TabPanelItem}
25006      */
25007     getTab : function(id){
25008         return this.items[id];
25009     },
25010
25011     /**
25012      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25013      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25014      */
25015     hideTab : function(id){
25016         var t = this.items[id];
25017         if(!t.isHidden()){
25018            t.setHidden(true);
25019            this.hiddenCount++;
25020            this.autoSizeTabs();
25021         }
25022     },
25023
25024     /**
25025      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25026      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25027      */
25028     unhideTab : function(id){
25029         var t = this.items[id];
25030         if(t.isHidden()){
25031            t.setHidden(false);
25032            this.hiddenCount--;
25033            this.autoSizeTabs();
25034         }
25035     },
25036
25037     /**
25038      * Adds an existing {@link Roo.TabPanelItem}.
25039      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25040      */
25041     addTabItem : function(item){
25042         this.items[item.id] = item;
25043         this.items.push(item);
25044         if(this.resizeTabs){
25045            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25046            this.autoSizeTabs();
25047         }else{
25048             item.autoSize();
25049         }
25050     },
25051
25052     /**
25053      * Removes a {@link Roo.TabPanelItem}.
25054      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25055      */
25056     removeTab : function(id){
25057         var items = this.items;
25058         var tab = items[id];
25059         if(!tab) return;
25060         var index = items.indexOf(tab);
25061         if(this.active == tab && items.length > 1){
25062             var newTab = this.getNextAvailable(index);
25063             if(newTab)newTab.activate();
25064         }
25065         this.stripEl.dom.removeChild(tab.pnode.dom);
25066         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25067             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25068         }
25069         items.splice(index, 1);
25070         delete this.items[tab.id];
25071         tab.fireEvent("close", tab);
25072         tab.purgeListeners();
25073         this.autoSizeTabs();
25074     },
25075
25076     getNextAvailable : function(start){
25077         var items = this.items;
25078         var index = start;
25079         // look for a next tab that will slide over to
25080         // replace the one being removed
25081         while(index < items.length){
25082             var item = items[++index];
25083             if(item && !item.isHidden()){
25084                 return item;
25085             }
25086         }
25087         // if one isn't found select the previous tab (on the left)
25088         index = start;
25089         while(index >= 0){
25090             var item = items[--index];
25091             if(item && !item.isHidden()){
25092                 return item;
25093             }
25094         }
25095         return null;
25096     },
25097
25098     /**
25099      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25100      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25101      */
25102     disableTab : function(id){
25103         var tab = this.items[id];
25104         if(tab && this.active != tab){
25105             tab.disable();
25106         }
25107     },
25108
25109     /**
25110      * Enables a {@link Roo.TabPanelItem} that is disabled.
25111      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25112      */
25113     enableTab : function(id){
25114         var tab = this.items[id];
25115         tab.enable();
25116     },
25117
25118     /**
25119      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25120      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25121      * @return {Roo.TabPanelItem} The TabPanelItem.
25122      */
25123     activate : function(id){
25124         var tab = this.items[id];
25125         if(!tab){
25126             return null;
25127         }
25128         if(tab == this.active || tab.disabled){
25129             return tab;
25130         }
25131         var e = {};
25132         this.fireEvent("beforetabchange", this, e, tab);
25133         if(e.cancel !== true && !tab.disabled){
25134             if(this.active){
25135                 this.active.hide();
25136             }
25137             this.active = this.items[id];
25138             this.active.show();
25139             this.fireEvent("tabchange", this, this.active);
25140         }
25141         return tab;
25142     },
25143
25144     /**
25145      * Gets the active {@link Roo.TabPanelItem}.
25146      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25147      */
25148     getActiveTab : function(){
25149         return this.active;
25150     },
25151
25152     /**
25153      * Updates the tab body element to fit the height of the container element
25154      * for overflow scrolling
25155      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25156      */
25157     syncHeight : function(targetHeight){
25158         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25159         var bm = this.bodyEl.getMargins();
25160         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25161         this.bodyEl.setHeight(newHeight);
25162         return newHeight;
25163     },
25164
25165     onResize : function(){
25166         if(this.monitorResize){
25167             this.autoSizeTabs();
25168         }
25169     },
25170
25171     /**
25172      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25173      */
25174     beginUpdate : function(){
25175         this.updating = true;
25176     },
25177
25178     /**
25179      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25180      */
25181     endUpdate : function(){
25182         this.updating = false;
25183         this.autoSizeTabs();
25184     },
25185
25186     /**
25187      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25188      */
25189     autoSizeTabs : function(){
25190         var count = this.items.length;
25191         var vcount = count - this.hiddenCount;
25192         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25193         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25194         var availWidth = Math.floor(w / vcount);
25195         var b = this.stripBody;
25196         if(b.getWidth() > w){
25197             var tabs = this.items;
25198             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25199             if(availWidth < this.minTabWidth){
25200                 /*if(!this.sleft){    // incomplete scrolling code
25201                     this.createScrollButtons();
25202                 }
25203                 this.showScroll();
25204                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25205             }
25206         }else{
25207             if(this.currentTabWidth < this.preferredTabWidth){
25208                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25209             }
25210         }
25211     },
25212
25213     /**
25214      * Returns the number of tabs in this TabPanel.
25215      * @return {Number}
25216      */
25217      getCount : function(){
25218          return this.items.length;
25219      },
25220
25221     /**
25222      * Resizes all the tabs to the passed width
25223      * @param {Number} The new width
25224      */
25225     setTabWidth : function(width){
25226         this.currentTabWidth = width;
25227         for(var i = 0, len = this.items.length; i < len; i++) {
25228                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25229         }
25230     },
25231
25232     /**
25233      * Destroys this TabPanel
25234      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25235      */
25236     destroy : function(removeEl){
25237         Roo.EventManager.removeResizeListener(this.onResize, this);
25238         for(var i = 0, len = this.items.length; i < len; i++){
25239             this.items[i].purgeListeners();
25240         }
25241         if(removeEl === true){
25242             this.el.update("");
25243             this.el.remove();
25244         }
25245     }
25246 });
25247
25248 /**
25249  * @class Roo.TabPanelItem
25250  * @extends Roo.util.Observable
25251  * Represents an individual item (tab plus body) in a TabPanel.
25252  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25253  * @param {String} id The id of this TabPanelItem
25254  * @param {String} text The text for the tab of this TabPanelItem
25255  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25256  */
25257 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25258     /**
25259      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25260      * @type Roo.TabPanel
25261      */
25262     this.tabPanel = tabPanel;
25263     /**
25264      * The id for this TabPanelItem
25265      * @type String
25266      */
25267     this.id = id;
25268     /** @private */
25269     this.disabled = false;
25270     /** @private */
25271     this.text = text;
25272     /** @private */
25273     this.loaded = false;
25274     this.closable = closable;
25275
25276     /**
25277      * The body element for this TabPanelItem.
25278      * @type Roo.Element
25279      */
25280     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25281     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25282     this.bodyEl.setStyle("display", "block");
25283     this.bodyEl.setStyle("zoom", "1");
25284     this.hideAction();
25285
25286     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25287     /** @private */
25288     this.el = Roo.get(els.el, true);
25289     this.inner = Roo.get(els.inner, true);
25290     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25291     this.pnode = Roo.get(els.el.parentNode, true);
25292     this.el.on("mousedown", this.onTabMouseDown, this);
25293     this.el.on("click", this.onTabClick, this);
25294     /** @private */
25295     if(closable){
25296         var c = Roo.get(els.close, true);
25297         c.dom.title = this.closeText;
25298         c.addClassOnOver("close-over");
25299         c.on("click", this.closeClick, this);
25300      }
25301
25302     this.addEvents({
25303          /**
25304          * @event activate
25305          * Fires when this tab becomes the active tab.
25306          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25307          * @param {Roo.TabPanelItem} this
25308          */
25309         "activate": true,
25310         /**
25311          * @event beforeclose
25312          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25313          * @param {Roo.TabPanelItem} this
25314          * @param {Object} e Set cancel to true on this object to cancel the close.
25315          */
25316         "beforeclose": true,
25317         /**
25318          * @event close
25319          * Fires when this tab is closed.
25320          * @param {Roo.TabPanelItem} this
25321          */
25322          "close": true,
25323         /**
25324          * @event deactivate
25325          * Fires when this tab is no longer the active tab.
25326          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25327          * @param {Roo.TabPanelItem} this
25328          */
25329          "deactivate" : true
25330     });
25331     this.hidden = false;
25332
25333     Roo.TabPanelItem.superclass.constructor.call(this);
25334 };
25335
25336 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25337     purgeListeners : function(){
25338        Roo.util.Observable.prototype.purgeListeners.call(this);
25339        this.el.removeAllListeners();
25340     },
25341     /**
25342      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25343      */
25344     show : function(){
25345         this.pnode.addClass("on");
25346         this.showAction();
25347         if(Roo.isOpera){
25348             this.tabPanel.stripWrap.repaint();
25349         }
25350         this.fireEvent("activate", this.tabPanel, this);
25351     },
25352
25353     /**
25354      * Returns true if this tab is the active tab.
25355      * @return {Boolean}
25356      */
25357     isActive : function(){
25358         return this.tabPanel.getActiveTab() == this;
25359     },
25360
25361     /**
25362      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25363      */
25364     hide : function(){
25365         this.pnode.removeClass("on");
25366         this.hideAction();
25367         this.fireEvent("deactivate", this.tabPanel, this);
25368     },
25369
25370     hideAction : function(){
25371         this.bodyEl.hide();
25372         this.bodyEl.setStyle("position", "absolute");
25373         this.bodyEl.setLeft("-20000px");
25374         this.bodyEl.setTop("-20000px");
25375     },
25376
25377     showAction : function(){
25378         this.bodyEl.setStyle("position", "relative");
25379         this.bodyEl.setTop("");
25380         this.bodyEl.setLeft("");
25381         this.bodyEl.show();
25382     },
25383
25384     /**
25385      * Set the tooltip for the tab.
25386      * @param {String} tooltip The tab's tooltip
25387      */
25388     setTooltip : function(text){
25389         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25390             this.textEl.dom.qtip = text;
25391             this.textEl.dom.removeAttribute('title');
25392         }else{
25393             this.textEl.dom.title = text;
25394         }
25395     },
25396
25397     onTabClick : function(e){
25398         e.preventDefault();
25399         this.tabPanel.activate(this.id);
25400     },
25401
25402     onTabMouseDown : function(e){
25403         e.preventDefault();
25404         this.tabPanel.activate(this.id);
25405     },
25406
25407     getWidth : function(){
25408         return this.inner.getWidth();
25409     },
25410
25411     setWidth : function(width){
25412         var iwidth = width - this.pnode.getPadding("lr");
25413         this.inner.setWidth(iwidth);
25414         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25415         this.pnode.setWidth(width);
25416     },
25417
25418     /**
25419      * Show or hide the tab
25420      * @param {Boolean} hidden True to hide or false to show.
25421      */
25422     setHidden : function(hidden){
25423         this.hidden = hidden;
25424         this.pnode.setStyle("display", hidden ? "none" : "");
25425     },
25426
25427     /**
25428      * Returns true if this tab is "hidden"
25429      * @return {Boolean}
25430      */
25431     isHidden : function(){
25432         return this.hidden;
25433     },
25434
25435     /**
25436      * Returns the text for this tab
25437      * @return {String}
25438      */
25439     getText : function(){
25440         return this.text;
25441     },
25442
25443     autoSize : function(){
25444         //this.el.beginMeasure();
25445         this.textEl.setWidth(1);
25446         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25447         //this.el.endMeasure();
25448     },
25449
25450     /**
25451      * Sets the text for the tab (Note: this also sets the tooltip text)
25452      * @param {String} text The tab's text and tooltip
25453      */
25454     setText : function(text){
25455         this.text = text;
25456         this.textEl.update(text);
25457         this.setTooltip(text);
25458         if(!this.tabPanel.resizeTabs){
25459             this.autoSize();
25460         }
25461     },
25462     /**
25463      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25464      */
25465     activate : function(){
25466         this.tabPanel.activate(this.id);
25467     },
25468
25469     /**
25470      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25471      */
25472     disable : function(){
25473         if(this.tabPanel.active != this){
25474             this.disabled = true;
25475             this.pnode.addClass("disabled");
25476         }
25477     },
25478
25479     /**
25480      * Enables this TabPanelItem if it was previously disabled.
25481      */
25482     enable : function(){
25483         this.disabled = false;
25484         this.pnode.removeClass("disabled");
25485     },
25486
25487     /**
25488      * Sets the content for this TabPanelItem.
25489      * @param {String} content The content
25490      * @param {Boolean} loadScripts true to look for and load scripts
25491      */
25492     setContent : function(content, loadScripts){
25493         this.bodyEl.update(content, loadScripts);
25494     },
25495
25496     /**
25497      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25498      * @return {Roo.UpdateManager} The UpdateManager
25499      */
25500     getUpdateManager : function(){
25501         return this.bodyEl.getUpdateManager();
25502     },
25503
25504     /**
25505      * Set a URL to be used to load the content for this TabPanelItem.
25506      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25507      * @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)
25508      * @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)
25509      * @return {Roo.UpdateManager} The UpdateManager
25510      */
25511     setUrl : function(url, params, loadOnce){
25512         if(this.refreshDelegate){
25513             this.un('activate', this.refreshDelegate);
25514         }
25515         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25516         this.on("activate", this.refreshDelegate);
25517         return this.bodyEl.getUpdateManager();
25518     },
25519
25520     /** @private */
25521     _handleRefresh : function(url, params, loadOnce){
25522         if(!loadOnce || !this.loaded){
25523             var updater = this.bodyEl.getUpdateManager();
25524             updater.update(url, params, this._setLoaded.createDelegate(this));
25525         }
25526     },
25527
25528     /**
25529      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25530      *   Will fail silently if the setUrl method has not been called.
25531      *   This does not activate the panel, just updates its content.
25532      */
25533     refresh : function(){
25534         if(this.refreshDelegate){
25535            this.loaded = false;
25536            this.refreshDelegate();
25537         }
25538     },
25539
25540     /** @private */
25541     _setLoaded : function(){
25542         this.loaded = true;
25543     },
25544
25545     /** @private */
25546     closeClick : function(e){
25547         var o = {};
25548         e.stopEvent();
25549         this.fireEvent("beforeclose", this, o);
25550         if(o.cancel !== true){
25551             this.tabPanel.removeTab(this.id);
25552         }
25553     },
25554     /**
25555      * The text displayed in the tooltip for the close icon.
25556      * @type String
25557      */
25558     closeText : "Close this tab"
25559 });
25560
25561 /** @private */
25562 Roo.TabPanel.prototype.createStrip = function(container){
25563     var strip = document.createElement("div");
25564     strip.className = "x-tabs-wrap";
25565     container.appendChild(strip);
25566     return strip;
25567 };
25568 /** @private */
25569 Roo.TabPanel.prototype.createStripList = function(strip){
25570     // div wrapper for retard IE
25571     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>';
25572     return strip.firstChild.firstChild.firstChild.firstChild;
25573 };
25574 /** @private */
25575 Roo.TabPanel.prototype.createBody = function(container){
25576     var body = document.createElement("div");
25577     Roo.id(body, "tab-body");
25578     Roo.fly(body).addClass("x-tabs-body");
25579     container.appendChild(body);
25580     return body;
25581 };
25582 /** @private */
25583 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25584     var body = Roo.getDom(id);
25585     if(!body){
25586         body = document.createElement("div");
25587         body.id = id;
25588     }
25589     Roo.fly(body).addClass("x-tabs-item-body");
25590     bodyEl.insertBefore(body, bodyEl.firstChild);
25591     return body;
25592 };
25593 /** @private */
25594 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25595     var td = document.createElement("td");
25596     stripEl.appendChild(td);
25597     if(closable){
25598         td.className = "x-tabs-closable";
25599         if(!this.closeTpl){
25600             this.closeTpl = new Roo.Template(
25601                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25602                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25603                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25604             );
25605         }
25606         var el = this.closeTpl.overwrite(td, {"text": text});
25607         var close = el.getElementsByTagName("div")[0];
25608         var inner = el.getElementsByTagName("em")[0];
25609         return {"el": el, "close": close, "inner": inner};
25610     } else {
25611         if(!this.tabTpl){
25612             this.tabTpl = new Roo.Template(
25613                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25614                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25615             );
25616         }
25617         var el = this.tabTpl.overwrite(td, {"text": text});
25618         var inner = el.getElementsByTagName("em")[0];
25619         return {"el": el, "inner": inner};
25620     }
25621 };/*
25622  * Based on:
25623  * Ext JS Library 1.1.1
25624  * Copyright(c) 2006-2007, Ext JS, LLC.
25625  *
25626  * Originally Released Under LGPL - original licence link has changed is not relivant.
25627  *
25628  * Fork - LGPL
25629  * <script type="text/javascript">
25630  */
25631
25632 /**
25633  * @class Roo.Button
25634  * @extends Roo.util.Observable
25635  * Simple Button class
25636  * @cfg {String} text The button text
25637  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25638  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25639  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25640  * @cfg {Object} scope The scope of the handler
25641  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25642  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25643  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25644  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25645  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25646  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25647    applies if enableToggle = true)
25648  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25649  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25650   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25651  * @constructor
25652  * Create a new button
25653  * @param {Object} config The config object
25654  */
25655 Roo.Button = function(renderTo, config)
25656 {
25657     if (!config) {
25658         config = renderTo;
25659         renderTo = config.renderTo || false;
25660     }
25661     
25662     Roo.apply(this, config);
25663     this.addEvents({
25664         /**
25665              * @event click
25666              * Fires when this button is clicked
25667              * @param {Button} this
25668              * @param {EventObject} e The click event
25669              */
25670             "click" : true,
25671         /**
25672              * @event toggle
25673              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25674              * @param {Button} this
25675              * @param {Boolean} pressed
25676              */
25677             "toggle" : true,
25678         /**
25679              * @event mouseover
25680              * Fires when the mouse hovers over the button
25681              * @param {Button} this
25682              * @param {Event} e The event object
25683              */
25684         'mouseover' : true,
25685         /**
25686              * @event mouseout
25687              * Fires when the mouse exits the button
25688              * @param {Button} this
25689              * @param {Event} e The event object
25690              */
25691         'mouseout': true,
25692          /**
25693              * @event render
25694              * Fires when the button is rendered
25695              * @param {Button} this
25696              */
25697         'render': true
25698     });
25699     if(this.menu){
25700         this.menu = Roo.menu.MenuMgr.get(this.menu);
25701     }
25702     if(renderTo){
25703         this.render(renderTo);
25704     }
25705     
25706     Roo.util.Observable.call(this);
25707 };
25708
25709 Roo.extend(Roo.Button, Roo.util.Observable, {
25710     /**
25711      * 
25712      */
25713     
25714     /**
25715      * Read-only. True if this button is hidden
25716      * @type Boolean
25717      */
25718     hidden : false,
25719     /**
25720      * Read-only. True if this button is disabled
25721      * @type Boolean
25722      */
25723     disabled : false,
25724     /**
25725      * Read-only. True if this button is pressed (only if enableToggle = true)
25726      * @type Boolean
25727      */
25728     pressed : false,
25729
25730     /**
25731      * @cfg {Number} tabIndex 
25732      * The DOM tabIndex for this button (defaults to undefined)
25733      */
25734     tabIndex : undefined,
25735
25736     /**
25737      * @cfg {Boolean} enableToggle
25738      * True to enable pressed/not pressed toggling (defaults to false)
25739      */
25740     enableToggle: false,
25741     /**
25742      * @cfg {Mixed} menu
25743      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25744      */
25745     menu : undefined,
25746     /**
25747      * @cfg {String} menuAlign
25748      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25749      */
25750     menuAlign : "tl-bl?",
25751
25752     /**
25753      * @cfg {String} iconCls
25754      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25755      */
25756     iconCls : undefined,
25757     /**
25758      * @cfg {String} type
25759      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25760      */
25761     type : 'button',
25762
25763     // private
25764     menuClassTarget: 'tr',
25765
25766     /**
25767      * @cfg {String} clickEvent
25768      * The type of event to map to the button's event handler (defaults to 'click')
25769      */
25770     clickEvent : 'click',
25771
25772     /**
25773      * @cfg {Boolean} handleMouseEvents
25774      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25775      */
25776     handleMouseEvents : true,
25777
25778     /**
25779      * @cfg {String} tooltipType
25780      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25781      */
25782     tooltipType : 'qtip',
25783
25784     /**
25785      * @cfg {String} cls
25786      * A CSS class to apply to the button's main element.
25787      */
25788     
25789     /**
25790      * @cfg {Roo.Template} template (Optional)
25791      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25792      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25793      * require code modifications if required elements (e.g. a button) aren't present.
25794      */
25795
25796     // private
25797     render : function(renderTo){
25798         var btn;
25799         if(this.hideParent){
25800             this.parentEl = Roo.get(renderTo);
25801         }
25802         if(!this.dhconfig){
25803             if(!this.template){
25804                 if(!Roo.Button.buttonTemplate){
25805                     // hideous table template
25806                     Roo.Button.buttonTemplate = new Roo.Template(
25807                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25808                         '<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>',
25809                         "</tr></tbody></table>");
25810                 }
25811                 this.template = Roo.Button.buttonTemplate;
25812             }
25813             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25814             var btnEl = btn.child("button:first");
25815             btnEl.on('focus', this.onFocus, this);
25816             btnEl.on('blur', this.onBlur, this);
25817             if(this.cls){
25818                 btn.addClass(this.cls);
25819             }
25820             if(this.icon){
25821                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25822             }
25823             if(this.iconCls){
25824                 btnEl.addClass(this.iconCls);
25825                 if(!this.cls){
25826                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25827                 }
25828             }
25829             if(this.tabIndex !== undefined){
25830                 btnEl.dom.tabIndex = this.tabIndex;
25831             }
25832             if(this.tooltip){
25833                 if(typeof this.tooltip == 'object'){
25834                     Roo.QuickTips.tips(Roo.apply({
25835                           target: btnEl.id
25836                     }, this.tooltip));
25837                 } else {
25838                     btnEl.dom[this.tooltipType] = this.tooltip;
25839                 }
25840             }
25841         }else{
25842             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25843         }
25844         this.el = btn;
25845         if(this.id){
25846             this.el.dom.id = this.el.id = this.id;
25847         }
25848         if(this.menu){
25849             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25850             this.menu.on("show", this.onMenuShow, this);
25851             this.menu.on("hide", this.onMenuHide, this);
25852         }
25853         btn.addClass("x-btn");
25854         if(Roo.isIE && !Roo.isIE7){
25855             this.autoWidth.defer(1, this);
25856         }else{
25857             this.autoWidth();
25858         }
25859         if(this.handleMouseEvents){
25860             btn.on("mouseover", this.onMouseOver, this);
25861             btn.on("mouseout", this.onMouseOut, this);
25862             btn.on("mousedown", this.onMouseDown, this);
25863         }
25864         btn.on(this.clickEvent, this.onClick, this);
25865         //btn.on("mouseup", this.onMouseUp, this);
25866         if(this.hidden){
25867             this.hide();
25868         }
25869         if(this.disabled){
25870             this.disable();
25871         }
25872         Roo.ButtonToggleMgr.register(this);
25873         if(this.pressed){
25874             this.el.addClass("x-btn-pressed");
25875         }
25876         if(this.repeat){
25877             var repeater = new Roo.util.ClickRepeater(btn,
25878                 typeof this.repeat == "object" ? this.repeat : {}
25879             );
25880             repeater.on("click", this.onClick,  this);
25881         }
25882         this.fireEvent('render', this);
25883         
25884     },
25885     /**
25886      * Returns the button's underlying element
25887      * @return {Roo.Element} The element
25888      */
25889     getEl : function(){
25890         return this.el;  
25891     },
25892     
25893     /**
25894      * Destroys this Button and removes any listeners.
25895      */
25896     destroy : function(){
25897         Roo.ButtonToggleMgr.unregister(this);
25898         this.el.removeAllListeners();
25899         this.purgeListeners();
25900         this.el.remove();
25901     },
25902
25903     // private
25904     autoWidth : function(){
25905         if(this.el){
25906             this.el.setWidth("auto");
25907             if(Roo.isIE7 && Roo.isStrict){
25908                 var ib = this.el.child('button');
25909                 if(ib && ib.getWidth() > 20){
25910                     ib.clip();
25911                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25912                 }
25913             }
25914             if(this.minWidth){
25915                 if(this.hidden){
25916                     this.el.beginMeasure();
25917                 }
25918                 if(this.el.getWidth() < this.minWidth){
25919                     this.el.setWidth(this.minWidth);
25920                 }
25921                 if(this.hidden){
25922                     this.el.endMeasure();
25923                 }
25924             }
25925         }
25926     },
25927
25928     /**
25929      * Assigns this button's click handler
25930      * @param {Function} handler The function to call when the button is clicked
25931      * @param {Object} scope (optional) Scope for the function passed in
25932      */
25933     setHandler : function(handler, scope){
25934         this.handler = handler;
25935         this.scope = scope;  
25936     },
25937     
25938     /**
25939      * Sets this button's text
25940      * @param {String} text The button text
25941      */
25942     setText : function(text){
25943         this.text = text;
25944         if(this.el){
25945             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25946         }
25947         this.autoWidth();
25948     },
25949     
25950     /**
25951      * Gets the text for this button
25952      * @return {String} The button text
25953      */
25954     getText : function(){
25955         return this.text;  
25956     },
25957     
25958     /**
25959      * Show this button
25960      */
25961     show: function(){
25962         this.hidden = false;
25963         if(this.el){
25964             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
25965         }
25966     },
25967     
25968     /**
25969      * Hide this button
25970      */
25971     hide: function(){
25972         this.hidden = true;
25973         if(this.el){
25974             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
25975         }
25976     },
25977     
25978     /**
25979      * Convenience function for boolean show/hide
25980      * @param {Boolean} visible True to show, false to hide
25981      */
25982     setVisible: function(visible){
25983         if(visible) {
25984             this.show();
25985         }else{
25986             this.hide();
25987         }
25988     },
25989     
25990     /**
25991      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
25992      * @param {Boolean} state (optional) Force a particular state
25993      */
25994     toggle : function(state){
25995         state = state === undefined ? !this.pressed : state;
25996         if(state != this.pressed){
25997             if(state){
25998                 this.el.addClass("x-btn-pressed");
25999                 this.pressed = true;
26000                 this.fireEvent("toggle", this, true);
26001             }else{
26002                 this.el.removeClass("x-btn-pressed");
26003                 this.pressed = false;
26004                 this.fireEvent("toggle", this, false);
26005             }
26006             if(this.toggleHandler){
26007                 this.toggleHandler.call(this.scope || this, this, state);
26008             }
26009         }
26010     },
26011     
26012     /**
26013      * Focus the button
26014      */
26015     focus : function(){
26016         this.el.child('button:first').focus();
26017     },
26018     
26019     /**
26020      * Disable this button
26021      */
26022     disable : function(){
26023         if(this.el){
26024             this.el.addClass("x-btn-disabled");
26025         }
26026         this.disabled = true;
26027     },
26028     
26029     /**
26030      * Enable this button
26031      */
26032     enable : function(){
26033         if(this.el){
26034             this.el.removeClass("x-btn-disabled");
26035         }
26036         this.disabled = false;
26037     },
26038
26039     /**
26040      * Convenience function for boolean enable/disable
26041      * @param {Boolean} enabled True to enable, false to disable
26042      */
26043     setDisabled : function(v){
26044         this[v !== true ? "enable" : "disable"]();
26045     },
26046
26047     // private
26048     onClick : function(e){
26049         if(e){
26050             e.preventDefault();
26051         }
26052         if(e.button != 0){
26053             return;
26054         }
26055         if(!this.disabled){
26056             if(this.enableToggle){
26057                 this.toggle();
26058             }
26059             if(this.menu && !this.menu.isVisible()){
26060                 this.menu.show(this.el, this.menuAlign);
26061             }
26062             this.fireEvent("click", this, e);
26063             if(this.handler){
26064                 this.el.removeClass("x-btn-over");
26065                 this.handler.call(this.scope || this, this, e);
26066             }
26067         }
26068     },
26069     // private
26070     onMouseOver : function(e){
26071         if(!this.disabled){
26072             this.el.addClass("x-btn-over");
26073             this.fireEvent('mouseover', this, e);
26074         }
26075     },
26076     // private
26077     onMouseOut : function(e){
26078         if(!e.within(this.el,  true)){
26079             this.el.removeClass("x-btn-over");
26080             this.fireEvent('mouseout', this, e);
26081         }
26082     },
26083     // private
26084     onFocus : function(e){
26085         if(!this.disabled){
26086             this.el.addClass("x-btn-focus");
26087         }
26088     },
26089     // private
26090     onBlur : function(e){
26091         this.el.removeClass("x-btn-focus");
26092     },
26093     // private
26094     onMouseDown : function(e){
26095         if(!this.disabled && e.button == 0){
26096             this.el.addClass("x-btn-click");
26097             Roo.get(document).on('mouseup', this.onMouseUp, this);
26098         }
26099     },
26100     // private
26101     onMouseUp : function(e){
26102         if(e.button == 0){
26103             this.el.removeClass("x-btn-click");
26104             Roo.get(document).un('mouseup', this.onMouseUp, this);
26105         }
26106     },
26107     // private
26108     onMenuShow : function(e){
26109         this.el.addClass("x-btn-menu-active");
26110     },
26111     // private
26112     onMenuHide : function(e){
26113         this.el.removeClass("x-btn-menu-active");
26114     }   
26115 });
26116
26117 // Private utility class used by Button
26118 Roo.ButtonToggleMgr = function(){
26119    var groups = {};
26120    
26121    function toggleGroup(btn, state){
26122        if(state){
26123            var g = groups[btn.toggleGroup];
26124            for(var i = 0, l = g.length; i < l; i++){
26125                if(g[i] != btn){
26126                    g[i].toggle(false);
26127                }
26128            }
26129        }
26130    }
26131    
26132    return {
26133        register : function(btn){
26134            if(!btn.toggleGroup){
26135                return;
26136            }
26137            var g = groups[btn.toggleGroup];
26138            if(!g){
26139                g = groups[btn.toggleGroup] = [];
26140            }
26141            g.push(btn);
26142            btn.on("toggle", toggleGroup);
26143        },
26144        
26145        unregister : function(btn){
26146            if(!btn.toggleGroup){
26147                return;
26148            }
26149            var g = groups[btn.toggleGroup];
26150            if(g){
26151                g.remove(btn);
26152                btn.un("toggle", toggleGroup);
26153            }
26154        }
26155    };
26156 }();/*
26157  * Based on:
26158  * Ext JS Library 1.1.1
26159  * Copyright(c) 2006-2007, Ext JS, LLC.
26160  *
26161  * Originally Released Under LGPL - original licence link has changed is not relivant.
26162  *
26163  * Fork - LGPL
26164  * <script type="text/javascript">
26165  */
26166  
26167 /**
26168  * @class Roo.SplitButton
26169  * @extends Roo.Button
26170  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26171  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26172  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26173  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26174  * @cfg {String} arrowTooltip The title attribute of the arrow
26175  * @constructor
26176  * Create a new menu button
26177  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26178  * @param {Object} config The config object
26179  */
26180 Roo.SplitButton = function(renderTo, config){
26181     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26182     /**
26183      * @event arrowclick
26184      * Fires when this button's arrow is clicked
26185      * @param {SplitButton} this
26186      * @param {EventObject} e The click event
26187      */
26188     this.addEvents({"arrowclick":true});
26189 };
26190
26191 Roo.extend(Roo.SplitButton, Roo.Button, {
26192     render : function(renderTo){
26193         // this is one sweet looking template!
26194         var tpl = new Roo.Template(
26195             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26196             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26197             '<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>',
26198             "</tbody></table></td><td>",
26199             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26200             '<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>',
26201             "</tbody></table></td></tr></table>"
26202         );
26203         var btn = tpl.append(renderTo, [this.text, this.type], true);
26204         var btnEl = btn.child("button");
26205         if(this.cls){
26206             btn.addClass(this.cls);
26207         }
26208         if(this.icon){
26209             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26210         }
26211         if(this.iconCls){
26212             btnEl.addClass(this.iconCls);
26213             if(!this.cls){
26214                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26215             }
26216         }
26217         this.el = btn;
26218         if(this.handleMouseEvents){
26219             btn.on("mouseover", this.onMouseOver, this);
26220             btn.on("mouseout", this.onMouseOut, this);
26221             btn.on("mousedown", this.onMouseDown, this);
26222             btn.on("mouseup", this.onMouseUp, this);
26223         }
26224         btn.on(this.clickEvent, this.onClick, this);
26225         if(this.tooltip){
26226             if(typeof this.tooltip == 'object'){
26227                 Roo.QuickTips.tips(Roo.apply({
26228                       target: btnEl.id
26229                 }, this.tooltip));
26230             } else {
26231                 btnEl.dom[this.tooltipType] = this.tooltip;
26232             }
26233         }
26234         if(this.arrowTooltip){
26235             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26236         }
26237         if(this.hidden){
26238             this.hide();
26239         }
26240         if(this.disabled){
26241             this.disable();
26242         }
26243         if(this.pressed){
26244             this.el.addClass("x-btn-pressed");
26245         }
26246         if(Roo.isIE && !Roo.isIE7){
26247             this.autoWidth.defer(1, this);
26248         }else{
26249             this.autoWidth();
26250         }
26251         if(this.menu){
26252             this.menu.on("show", this.onMenuShow, this);
26253             this.menu.on("hide", this.onMenuHide, this);
26254         }
26255         this.fireEvent('render', this);
26256     },
26257
26258     // private
26259     autoWidth : function(){
26260         if(this.el){
26261             var tbl = this.el.child("table:first");
26262             var tbl2 = this.el.child("table:last");
26263             this.el.setWidth("auto");
26264             tbl.setWidth("auto");
26265             if(Roo.isIE7 && Roo.isStrict){
26266                 var ib = this.el.child('button:first');
26267                 if(ib && ib.getWidth() > 20){
26268                     ib.clip();
26269                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26270                 }
26271             }
26272             if(this.minWidth){
26273                 if(this.hidden){
26274                     this.el.beginMeasure();
26275                 }
26276                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26277                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26278                 }
26279                 if(this.hidden){
26280                     this.el.endMeasure();
26281                 }
26282             }
26283             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26284         } 
26285     },
26286     /**
26287      * Sets this button's click handler
26288      * @param {Function} handler The function to call when the button is clicked
26289      * @param {Object} scope (optional) Scope for the function passed above
26290      */
26291     setHandler : function(handler, scope){
26292         this.handler = handler;
26293         this.scope = scope;  
26294     },
26295     
26296     /**
26297      * Sets this button's arrow click handler
26298      * @param {Function} handler The function to call when the arrow is clicked
26299      * @param {Object} scope (optional) Scope for the function passed above
26300      */
26301     setArrowHandler : function(handler, scope){
26302         this.arrowHandler = handler;
26303         this.scope = scope;  
26304     },
26305     
26306     /**
26307      * Focus the button
26308      */
26309     focus : function(){
26310         if(this.el){
26311             this.el.child("button:first").focus();
26312         }
26313     },
26314
26315     // private
26316     onClick : function(e){
26317         e.preventDefault();
26318         if(!this.disabled){
26319             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26320                 if(this.menu && !this.menu.isVisible()){
26321                     this.menu.show(this.el, this.menuAlign);
26322                 }
26323                 this.fireEvent("arrowclick", this, e);
26324                 if(this.arrowHandler){
26325                     this.arrowHandler.call(this.scope || this, this, e);
26326                 }
26327             }else{
26328                 this.fireEvent("click", this, e);
26329                 if(this.handler){
26330                     this.handler.call(this.scope || this, this, e);
26331                 }
26332             }
26333         }
26334     },
26335     // private
26336     onMouseDown : function(e){
26337         if(!this.disabled){
26338             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26339         }
26340     },
26341     // private
26342     onMouseUp : function(e){
26343         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26344     }   
26345 });
26346
26347
26348 // backwards compat
26349 Roo.MenuButton = Roo.SplitButton;/*
26350  * Based on:
26351  * Ext JS Library 1.1.1
26352  * Copyright(c) 2006-2007, Ext JS, LLC.
26353  *
26354  * Originally Released Under LGPL - original licence link has changed is not relivant.
26355  *
26356  * Fork - LGPL
26357  * <script type="text/javascript">
26358  */
26359
26360 /**
26361  * @class Roo.Toolbar
26362  * Basic Toolbar class.
26363  * @constructor
26364  * Creates a new Toolbar
26365  * @param {Object} config The config object
26366  */ 
26367 Roo.Toolbar = function(container, buttons, config)
26368 {
26369     /// old consturctor format still supported..
26370     if(container instanceof Array){ // omit the container for later rendering
26371         buttons = container;
26372         config = buttons;
26373         container = null;
26374     }
26375     if (typeof(container) == 'object' && container.xtype) {
26376         config = container;
26377         container = config.container;
26378         buttons = config.buttons; // not really - use items!!
26379     }
26380     var xitems = [];
26381     if (config && config.items) {
26382         xitems = config.items;
26383         delete config.items;
26384     }
26385     Roo.apply(this, config);
26386     this.buttons = buttons;
26387     
26388     if(container){
26389         this.render(container);
26390     }
26391     Roo.each(xitems, function(b) {
26392         this.add(b);
26393     }, this);
26394     
26395 };
26396
26397 Roo.Toolbar.prototype = {
26398     /**
26399      * @cfg {Roo.data.Store} items
26400      * array of button configs or elements to add
26401      */
26402     
26403     /**
26404      * @cfg {String/HTMLElement/Element} container
26405      * The id or element that will contain the toolbar
26406      */
26407     // private
26408     render : function(ct){
26409         this.el = Roo.get(ct);
26410         if(this.cls){
26411             this.el.addClass(this.cls);
26412         }
26413         // using a table allows for vertical alignment
26414         // 100% width is needed by Safari...
26415         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26416         this.tr = this.el.child("tr", true);
26417         var autoId = 0;
26418         this.items = new Roo.util.MixedCollection(false, function(o){
26419             return o.id || ("item" + (++autoId));
26420         });
26421         if(this.buttons){
26422             this.add.apply(this, this.buttons);
26423             delete this.buttons;
26424         }
26425     },
26426
26427     /**
26428      * Adds element(s) to the toolbar -- this function takes a variable number of 
26429      * arguments of mixed type and adds them to the toolbar.
26430      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26431      * <ul>
26432      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26433      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26434      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26435      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26436      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26437      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26438      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26439      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26440      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26441      * </ul>
26442      * @param {Mixed} arg2
26443      * @param {Mixed} etc.
26444      */
26445     add : function(){
26446         var a = arguments, l = a.length;
26447         for(var i = 0; i < l; i++){
26448             this._add(a[i]);
26449         }
26450     },
26451     // private..
26452     _add : function(el) {
26453         
26454         if (el.xtype) {
26455             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26456         }
26457         
26458         if (el.applyTo){ // some kind of form field
26459             return this.addField(el);
26460         } 
26461         if (el.render){ // some kind of Toolbar.Item
26462             return this.addItem(el);
26463         }
26464         if (typeof el == "string"){ // string
26465             if(el == "separator" || el == "-"){
26466                 return this.addSeparator();
26467             }
26468             if (el == " "){
26469                 return this.addSpacer();
26470             }
26471             if(el == "->"){
26472                 return this.addFill();
26473             }
26474             return this.addText(el);
26475             
26476         }
26477         if(el.tagName){ // element
26478             return this.addElement(el);
26479         }
26480         if(typeof el == "object"){ // must be button config?
26481             return this.addButton(el);
26482         }
26483         // and now what?!?!
26484         return false;
26485         
26486     },
26487     
26488     /**
26489      * Add an Xtype element
26490      * @param {Object} xtype Xtype Object
26491      * @return {Object} created Object
26492      */
26493     addxtype : function(e){
26494         return this.add(e);  
26495     },
26496     
26497     /**
26498      * Returns the Element for this toolbar.
26499      * @return {Roo.Element}
26500      */
26501     getEl : function(){
26502         return this.el;  
26503     },
26504     
26505     /**
26506      * Adds a separator
26507      * @return {Roo.Toolbar.Item} The separator item
26508      */
26509     addSeparator : function(){
26510         return this.addItem(new Roo.Toolbar.Separator());
26511     },
26512
26513     /**
26514      * Adds a spacer element
26515      * @return {Roo.Toolbar.Spacer} The spacer item
26516      */
26517     addSpacer : function(){
26518         return this.addItem(new Roo.Toolbar.Spacer());
26519     },
26520
26521     /**
26522      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26523      * @return {Roo.Toolbar.Fill} The fill item
26524      */
26525     addFill : function(){
26526         return this.addItem(new Roo.Toolbar.Fill());
26527     },
26528
26529     /**
26530      * Adds any standard HTML element to the toolbar
26531      * @param {String/HTMLElement/Element} el The element or id of the element to add
26532      * @return {Roo.Toolbar.Item} The element's item
26533      */
26534     addElement : function(el){
26535         return this.addItem(new Roo.Toolbar.Item(el));
26536     },
26537     /**
26538      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26539      * @type Roo.util.MixedCollection  
26540      */
26541     items : false,
26542      
26543     /**
26544      * Adds any Toolbar.Item or subclass
26545      * @param {Roo.Toolbar.Item} item
26546      * @return {Roo.Toolbar.Item} The item
26547      */
26548     addItem : function(item){
26549         var td = this.nextBlock();
26550         item.render(td);
26551         this.items.add(item);
26552         return item;
26553     },
26554     
26555     /**
26556      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26557      * @param {Object/Array} config A button config or array of configs
26558      * @return {Roo.Toolbar.Button/Array}
26559      */
26560     addButton : function(config){
26561         if(config instanceof Array){
26562             var buttons = [];
26563             for(var i = 0, len = config.length; i < len; i++) {
26564                 buttons.push(this.addButton(config[i]));
26565             }
26566             return buttons;
26567         }
26568         var b = config;
26569         if(!(config instanceof Roo.Toolbar.Button)){
26570             b = config.split ?
26571                 new Roo.Toolbar.SplitButton(config) :
26572                 new Roo.Toolbar.Button(config);
26573         }
26574         var td = this.nextBlock();
26575         b.render(td);
26576         this.items.add(b);
26577         return b;
26578     },
26579     
26580     /**
26581      * Adds text to the toolbar
26582      * @param {String} text The text to add
26583      * @return {Roo.Toolbar.Item} The element's item
26584      */
26585     addText : function(text){
26586         return this.addItem(new Roo.Toolbar.TextItem(text));
26587     },
26588     
26589     /**
26590      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26591      * @param {Number} index The index where the item is to be inserted
26592      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26593      * @return {Roo.Toolbar.Button/Item}
26594      */
26595     insertButton : function(index, item){
26596         if(item instanceof Array){
26597             var buttons = [];
26598             for(var i = 0, len = item.length; i < len; i++) {
26599                buttons.push(this.insertButton(index + i, item[i]));
26600             }
26601             return buttons;
26602         }
26603         if (!(item instanceof Roo.Toolbar.Button)){
26604            item = new Roo.Toolbar.Button(item);
26605         }
26606         var td = document.createElement("td");
26607         this.tr.insertBefore(td, this.tr.childNodes[index]);
26608         item.render(td);
26609         this.items.insert(index, item);
26610         return item;
26611     },
26612     
26613     /**
26614      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26615      * @param {Object} config
26616      * @return {Roo.Toolbar.Item} The element's item
26617      */
26618     addDom : function(config, returnEl){
26619         var td = this.nextBlock();
26620         Roo.DomHelper.overwrite(td, config);
26621         var ti = new Roo.Toolbar.Item(td.firstChild);
26622         ti.render(td);
26623         this.items.add(ti);
26624         return ti;
26625     },
26626
26627     /**
26628      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26629      * @type Roo.util.MixedCollection  
26630      */
26631     fields : false,
26632     
26633     /**
26634      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26635      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26636      * @param {Roo.form.Field} field
26637      * @return {Roo.ToolbarItem}
26638      */
26639      
26640       
26641     addField : function(field) {
26642         if (!this.fields) {
26643             var autoId = 0;
26644             this.fields = new Roo.util.MixedCollection(false, function(o){
26645                 return o.id || ("item" + (++autoId));
26646             });
26647
26648         }
26649         
26650         var td = this.nextBlock();
26651         field.render(td);
26652         var ti = new Roo.Toolbar.Item(td.firstChild);
26653         ti.render(td);
26654         this.items.add(ti);
26655         this.fields.add(field);
26656         return ti;
26657     },
26658     /**
26659      * Hide the toolbar
26660      * @method hide
26661      */
26662      
26663       
26664     hide : function()
26665     {
26666         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26667         this.el.child('div').hide();
26668     },
26669     /**
26670      * Show the toolbar
26671      * @method show
26672      */
26673     show : function()
26674     {
26675         this.el.child('div').show();
26676     },
26677       
26678     // private
26679     nextBlock : function(){
26680         var td = document.createElement("td");
26681         this.tr.appendChild(td);
26682         return td;
26683     },
26684
26685     // private
26686     destroy : function(){
26687         if(this.items){ // rendered?
26688             Roo.destroy.apply(Roo, this.items.items);
26689         }
26690         if(this.fields){ // rendered?
26691             Roo.destroy.apply(Roo, this.fields.items);
26692         }
26693         Roo.Element.uncache(this.el, this.tr);
26694     }
26695 };
26696
26697 /**
26698  * @class Roo.Toolbar.Item
26699  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26700  * @constructor
26701  * Creates a new Item
26702  * @param {HTMLElement} el 
26703  */
26704 Roo.Toolbar.Item = function(el){
26705     this.el = Roo.getDom(el);
26706     this.id = Roo.id(this.el);
26707     this.hidden = false;
26708 };
26709
26710 Roo.Toolbar.Item.prototype = {
26711     
26712     /**
26713      * Get this item's HTML Element
26714      * @return {HTMLElement}
26715      */
26716     getEl : function(){
26717        return this.el;  
26718     },
26719
26720     // private
26721     render : function(td){
26722         this.td = td;
26723         td.appendChild(this.el);
26724     },
26725     
26726     /**
26727      * Removes and destroys this item.
26728      */
26729     destroy : function(){
26730         this.td.parentNode.removeChild(this.td);
26731     },
26732     
26733     /**
26734      * Shows this item.
26735      */
26736     show: function(){
26737         this.hidden = false;
26738         this.td.style.display = "";
26739     },
26740     
26741     /**
26742      * Hides this item.
26743      */
26744     hide: function(){
26745         this.hidden = true;
26746         this.td.style.display = "none";
26747     },
26748     
26749     /**
26750      * Convenience function for boolean show/hide.
26751      * @param {Boolean} visible true to show/false to hide
26752      */
26753     setVisible: function(visible){
26754         if(visible) {
26755             this.show();
26756         }else{
26757             this.hide();
26758         }
26759     },
26760     
26761     /**
26762      * Try to focus this item.
26763      */
26764     focus : function(){
26765         Roo.fly(this.el).focus();
26766     },
26767     
26768     /**
26769      * Disables this item.
26770      */
26771     disable : function(){
26772         Roo.fly(this.td).addClass("x-item-disabled");
26773         this.disabled = true;
26774         this.el.disabled = true;
26775     },
26776     
26777     /**
26778      * Enables this item.
26779      */
26780     enable : function(){
26781         Roo.fly(this.td).removeClass("x-item-disabled");
26782         this.disabled = false;
26783         this.el.disabled = false;
26784     }
26785 };
26786
26787
26788 /**
26789  * @class Roo.Toolbar.Separator
26790  * @extends Roo.Toolbar.Item
26791  * A simple toolbar separator class
26792  * @constructor
26793  * Creates a new Separator
26794  */
26795 Roo.Toolbar.Separator = function(){
26796     var s = document.createElement("span");
26797     s.className = "ytb-sep";
26798     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26799 };
26800 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26801     enable:Roo.emptyFn,
26802     disable:Roo.emptyFn,
26803     focus:Roo.emptyFn
26804 });
26805
26806 /**
26807  * @class Roo.Toolbar.Spacer
26808  * @extends Roo.Toolbar.Item
26809  * A simple element that adds extra horizontal space to a toolbar.
26810  * @constructor
26811  * Creates a new Spacer
26812  */
26813 Roo.Toolbar.Spacer = function(){
26814     var s = document.createElement("div");
26815     s.className = "ytb-spacer";
26816     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26817 };
26818 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26819     enable:Roo.emptyFn,
26820     disable:Roo.emptyFn,
26821     focus:Roo.emptyFn
26822 });
26823
26824 /**
26825  * @class Roo.Toolbar.Fill
26826  * @extends Roo.Toolbar.Spacer
26827  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26828  * @constructor
26829  * Creates a new Spacer
26830  */
26831 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26832     // private
26833     render : function(td){
26834         td.style.width = '100%';
26835         Roo.Toolbar.Fill.superclass.render.call(this, td);
26836     }
26837 });
26838
26839 /**
26840  * @class Roo.Toolbar.TextItem
26841  * @extends Roo.Toolbar.Item
26842  * A simple class that renders text directly into a toolbar.
26843  * @constructor
26844  * Creates a new TextItem
26845  * @param {String} text
26846  */
26847 Roo.Toolbar.TextItem = function(text){
26848     if (typeof(text) == 'object') {
26849         text = text.text;
26850     }
26851     var s = document.createElement("span");
26852     s.className = "ytb-text";
26853     s.innerHTML = text;
26854     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26855 };
26856 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26857     enable:Roo.emptyFn,
26858     disable:Roo.emptyFn,
26859     focus:Roo.emptyFn
26860 });
26861
26862 /**
26863  * @class Roo.Toolbar.Button
26864  * @extends Roo.Button
26865  * A button that renders into a toolbar.
26866  * @constructor
26867  * Creates a new Button
26868  * @param {Object} config A standard {@link Roo.Button} config object
26869  */
26870 Roo.Toolbar.Button = function(config){
26871     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26872 };
26873 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26874     render : function(td){
26875         this.td = td;
26876         Roo.Toolbar.Button.superclass.render.call(this, td);
26877     },
26878     
26879     /**
26880      * Removes and destroys this button
26881      */
26882     destroy : function(){
26883         Roo.Toolbar.Button.superclass.destroy.call(this);
26884         this.td.parentNode.removeChild(this.td);
26885     },
26886     
26887     /**
26888      * Shows this button
26889      */
26890     show: function(){
26891         this.hidden = false;
26892         this.td.style.display = "";
26893     },
26894     
26895     /**
26896      * Hides this button
26897      */
26898     hide: function(){
26899         this.hidden = true;
26900         this.td.style.display = "none";
26901     },
26902
26903     /**
26904      * Disables this item
26905      */
26906     disable : function(){
26907         Roo.fly(this.td).addClass("x-item-disabled");
26908         this.disabled = true;
26909     },
26910
26911     /**
26912      * Enables this item
26913      */
26914     enable : function(){
26915         Roo.fly(this.td).removeClass("x-item-disabled");
26916         this.disabled = false;
26917     }
26918 });
26919 // backwards compat
26920 Roo.ToolbarButton = Roo.Toolbar.Button;
26921
26922 /**
26923  * @class Roo.Toolbar.SplitButton
26924  * @extends Roo.SplitButton
26925  * A menu button that renders into a toolbar.
26926  * @constructor
26927  * Creates a new SplitButton
26928  * @param {Object} config A standard {@link Roo.SplitButton} config object
26929  */
26930 Roo.Toolbar.SplitButton = function(config){
26931     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26932 };
26933 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26934     render : function(td){
26935         this.td = td;
26936         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26937     },
26938     
26939     /**
26940      * Removes and destroys this button
26941      */
26942     destroy : function(){
26943         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26944         this.td.parentNode.removeChild(this.td);
26945     },
26946     
26947     /**
26948      * Shows this button
26949      */
26950     show: function(){
26951         this.hidden = false;
26952         this.td.style.display = "";
26953     },
26954     
26955     /**
26956      * Hides this button
26957      */
26958     hide: function(){
26959         this.hidden = true;
26960         this.td.style.display = "none";
26961     }
26962 });
26963
26964 // backwards compat
26965 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
26966  * Based on:
26967  * Ext JS Library 1.1.1
26968  * Copyright(c) 2006-2007, Ext JS, LLC.
26969  *
26970  * Originally Released Under LGPL - original licence link has changed is not relivant.
26971  *
26972  * Fork - LGPL
26973  * <script type="text/javascript">
26974  */
26975  
26976 /**
26977  * @class Roo.PagingToolbar
26978  * @extends Roo.Toolbar
26979  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26980  * @constructor
26981  * Create a new PagingToolbar
26982  * @param {Object} config The config object
26983  */
26984 Roo.PagingToolbar = function(el, ds, config)
26985 {
26986     // old args format still supported... - xtype is prefered..
26987     if (typeof(el) == 'object' && el.xtype) {
26988         // created from xtype...
26989         config = el;
26990         ds = el.dataSource;
26991         el = config.container;
26992     }
26993     
26994     
26995     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
26996     this.ds = ds;
26997     this.cursor = 0;
26998     this.renderButtons(this.el);
26999     this.bind(ds);
27000 };
27001
27002 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27003     /**
27004      * @cfg {Roo.data.Store} dataSource
27005      * The underlying data store providing the paged data
27006      */
27007     /**
27008      * @cfg {String/HTMLElement/Element} container
27009      * container The id or element that will contain the toolbar
27010      */
27011     /**
27012      * @cfg {Boolean} displayInfo
27013      * True to display the displayMsg (defaults to false)
27014      */
27015     /**
27016      * @cfg {Number} pageSize
27017      * The number of records to display per page (defaults to 20)
27018      */
27019     pageSize: 20,
27020     /**
27021      * @cfg {String} displayMsg
27022      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27023      */
27024     displayMsg : 'Displaying {0} - {1} of {2}',
27025     /**
27026      * @cfg {String} emptyMsg
27027      * The message to display when no records are found (defaults to "No data to display")
27028      */
27029     emptyMsg : 'No data to display',
27030     /**
27031      * Customizable piece of the default paging text (defaults to "Page")
27032      * @type String
27033      */
27034     beforePageText : "Page",
27035     /**
27036      * Customizable piece of the default paging text (defaults to "of %0")
27037      * @type String
27038      */
27039     afterPageText : "of {0}",
27040     /**
27041      * Customizable piece of the default paging text (defaults to "First Page")
27042      * @type String
27043      */
27044     firstText : "First Page",
27045     /**
27046      * Customizable piece of the default paging text (defaults to "Previous Page")
27047      * @type String
27048      */
27049     prevText : "Previous Page",
27050     /**
27051      * Customizable piece of the default paging text (defaults to "Next Page")
27052      * @type String
27053      */
27054     nextText : "Next Page",
27055     /**
27056      * Customizable piece of the default paging text (defaults to "Last Page")
27057      * @type String
27058      */
27059     lastText : "Last Page",
27060     /**
27061      * Customizable piece of the default paging text (defaults to "Refresh")
27062      * @type String
27063      */
27064     refreshText : "Refresh",
27065
27066     // private
27067     renderButtons : function(el){
27068         Roo.PagingToolbar.superclass.render.call(this, el);
27069         this.first = this.addButton({
27070             tooltip: this.firstText,
27071             cls: "x-btn-icon x-grid-page-first",
27072             disabled: true,
27073             handler: this.onClick.createDelegate(this, ["first"])
27074         });
27075         this.prev = this.addButton({
27076             tooltip: this.prevText,
27077             cls: "x-btn-icon x-grid-page-prev",
27078             disabled: true,
27079             handler: this.onClick.createDelegate(this, ["prev"])
27080         });
27081         this.addSeparator();
27082         this.add(this.beforePageText);
27083         this.field = Roo.get(this.addDom({
27084            tag: "input",
27085            type: "text",
27086            size: "3",
27087            value: "1",
27088            cls: "x-grid-page-number"
27089         }).el);
27090         this.field.on("keydown", this.onPagingKeydown, this);
27091         this.field.on("focus", function(){this.dom.select();});
27092         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27093         this.field.setHeight(18);
27094         this.addSeparator();
27095         this.next = this.addButton({
27096             tooltip: this.nextText,
27097             cls: "x-btn-icon x-grid-page-next",
27098             disabled: true,
27099             handler: this.onClick.createDelegate(this, ["next"])
27100         });
27101         this.last = this.addButton({
27102             tooltip: this.lastText,
27103             cls: "x-btn-icon x-grid-page-last",
27104             disabled: true,
27105             handler: this.onClick.createDelegate(this, ["last"])
27106         });
27107         this.addSeparator();
27108         this.loading = this.addButton({
27109             tooltip: this.refreshText,
27110             cls: "x-btn-icon x-grid-loading",
27111             handler: this.onClick.createDelegate(this, ["refresh"])
27112         });
27113
27114         if(this.displayInfo){
27115             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27116         }
27117     },
27118
27119     // private
27120     updateInfo : function(){
27121         if(this.displayEl){
27122             var count = this.ds.getCount();
27123             var msg = count == 0 ?
27124                 this.emptyMsg :
27125                 String.format(
27126                     this.displayMsg,
27127                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27128                 );
27129             this.displayEl.update(msg);
27130         }
27131     },
27132
27133     // private
27134     onLoad : function(ds, r, o){
27135        this.cursor = o.params ? o.params.start : 0;
27136        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27137
27138        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27139        this.field.dom.value = ap;
27140        this.first.setDisabled(ap == 1);
27141        this.prev.setDisabled(ap == 1);
27142        this.next.setDisabled(ap == ps);
27143        this.last.setDisabled(ap == ps);
27144        this.loading.enable();
27145        this.updateInfo();
27146     },
27147
27148     // private
27149     getPageData : function(){
27150         var total = this.ds.getTotalCount();
27151         return {
27152             total : total,
27153             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27154             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27155         };
27156     },
27157
27158     // private
27159     onLoadError : function(){
27160         this.loading.enable();
27161     },
27162
27163     // private
27164     onPagingKeydown : function(e){
27165         var k = e.getKey();
27166         var d = this.getPageData();
27167         if(k == e.RETURN){
27168             var v = this.field.dom.value, pageNum;
27169             if(!v || isNaN(pageNum = parseInt(v, 10))){
27170                 this.field.dom.value = d.activePage;
27171                 return;
27172             }
27173             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27174             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27175             e.stopEvent();
27176         }
27177         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))
27178         {
27179           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27180           this.field.dom.value = pageNum;
27181           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27182           e.stopEvent();
27183         }
27184         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27185         {
27186           var v = this.field.dom.value, pageNum; 
27187           var increment = (e.shiftKey) ? 10 : 1;
27188           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27189             increment *= -1;
27190           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27191             this.field.dom.value = d.activePage;
27192             return;
27193           }
27194           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27195           {
27196             this.field.dom.value = parseInt(v, 10) + increment;
27197             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27198             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27199           }
27200           e.stopEvent();
27201         }
27202     },
27203
27204     // private
27205     beforeLoad : function(){
27206         if(this.loading){
27207             this.loading.disable();
27208         }
27209     },
27210
27211     // private
27212     onClick : function(which){
27213         var ds = this.ds;
27214         switch(which){
27215             case "first":
27216                 ds.load({params:{start: 0, limit: this.pageSize}});
27217             break;
27218             case "prev":
27219                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27220             break;
27221             case "next":
27222                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27223             break;
27224             case "last":
27225                 var total = ds.getTotalCount();
27226                 var extra = total % this.pageSize;
27227                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27228                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27229             break;
27230             case "refresh":
27231                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27232             break;
27233         }
27234     },
27235
27236     /**
27237      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27238      * @param {Roo.data.Store} store The data store to unbind
27239      */
27240     unbind : function(ds){
27241         ds.un("beforeload", this.beforeLoad, this);
27242         ds.un("load", this.onLoad, this);
27243         ds.un("loadexception", this.onLoadError, this);
27244         ds.un("remove", this.updateInfo, this);
27245         ds.un("add", this.updateInfo, this);
27246         this.ds = undefined;
27247     },
27248
27249     /**
27250      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27251      * @param {Roo.data.Store} store The data store to bind
27252      */
27253     bind : function(ds){
27254         ds.on("beforeload", this.beforeLoad, this);
27255         ds.on("load", this.onLoad, this);
27256         ds.on("loadexception", this.onLoadError, this);
27257         ds.on("remove", this.updateInfo, this);
27258         ds.on("add", this.updateInfo, this);
27259         this.ds = ds;
27260     }
27261 });/*
27262  * Based on:
27263  * Ext JS Library 1.1.1
27264  * Copyright(c) 2006-2007, Ext JS, LLC.
27265  *
27266  * Originally Released Under LGPL - original licence link has changed is not relivant.
27267  *
27268  * Fork - LGPL
27269  * <script type="text/javascript">
27270  */
27271
27272 /**
27273  * @class Roo.Resizable
27274  * @extends Roo.util.Observable
27275  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27276  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27277  * 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
27278  * the element will be wrapped for you automatically.</p>
27279  * <p>Here is the list of valid resize handles:</p>
27280  * <pre>
27281 Value   Description
27282 ------  -------------------
27283  'n'     north
27284  's'     south
27285  'e'     east
27286  'w'     west
27287  'nw'    northwest
27288  'sw'    southwest
27289  'se'    southeast
27290  'ne'    northeast
27291  'all'   all
27292 </pre>
27293  * <p>Here's an example showing the creation of a typical Resizable:</p>
27294  * <pre><code>
27295 var resizer = new Roo.Resizable("element-id", {
27296     handles: 'all',
27297     minWidth: 200,
27298     minHeight: 100,
27299     maxWidth: 500,
27300     maxHeight: 400,
27301     pinned: true
27302 });
27303 resizer.on("resize", myHandler);
27304 </code></pre>
27305  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27306  * resizer.east.setDisplayed(false);</p>
27307  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27308  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27309  * resize operation's new size (defaults to [0, 0])
27310  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27311  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27312  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27313  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27314  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27315  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27316  * @cfg {Number} width The width of the element in pixels (defaults to null)
27317  * @cfg {Number} height The height of the element in pixels (defaults to null)
27318  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27319  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27320  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27321  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27322  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27323  * in favor of the handles config option (defaults to false)
27324  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27325  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27326  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27327  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27328  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27329  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27330  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27331  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27332  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27333  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27334  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27335  * @constructor
27336  * Create a new resizable component
27337  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27338  * @param {Object} config configuration options
27339   */
27340 Roo.Resizable = function(el, config){
27341     this.el = Roo.get(el);
27342
27343     if(config && config.wrap){
27344         config.resizeChild = this.el;
27345         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27346         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27347         this.el.setStyle("overflow", "hidden");
27348         this.el.setPositioning(config.resizeChild.getPositioning());
27349         config.resizeChild.clearPositioning();
27350         if(!config.width || !config.height){
27351             var csize = config.resizeChild.getSize();
27352             this.el.setSize(csize.width, csize.height);
27353         }
27354         if(config.pinned && !config.adjustments){
27355             config.adjustments = "auto";
27356         }
27357     }
27358
27359     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27360     this.proxy.unselectable();
27361     this.proxy.enableDisplayMode('block');
27362
27363     Roo.apply(this, config);
27364
27365     if(this.pinned){
27366         this.disableTrackOver = true;
27367         this.el.addClass("x-resizable-pinned");
27368     }
27369     // if the element isn't positioned, make it relative
27370     var position = this.el.getStyle("position");
27371     if(position != "absolute" && position != "fixed"){
27372         this.el.setStyle("position", "relative");
27373     }
27374     if(!this.handles){ // no handles passed, must be legacy style
27375         this.handles = 's,e,se';
27376         if(this.multiDirectional){
27377             this.handles += ',n,w';
27378         }
27379     }
27380     if(this.handles == "all"){
27381         this.handles = "n s e w ne nw se sw";
27382     }
27383     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27384     var ps = Roo.Resizable.positions;
27385     for(var i = 0, len = hs.length; i < len; i++){
27386         if(hs[i] && ps[hs[i]]){
27387             var pos = ps[hs[i]];
27388             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27389         }
27390     }
27391     // legacy
27392     this.corner = this.southeast;
27393
27394     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27395         this.updateBox = true;
27396     }
27397
27398     this.activeHandle = null;
27399
27400     if(this.resizeChild){
27401         if(typeof this.resizeChild == "boolean"){
27402             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27403         }else{
27404             this.resizeChild = Roo.get(this.resizeChild, true);
27405         }
27406     }
27407
27408     if(this.adjustments == "auto"){
27409         var rc = this.resizeChild;
27410         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27411         if(rc && (hw || hn)){
27412             rc.position("relative");
27413             rc.setLeft(hw ? hw.el.getWidth() : 0);
27414             rc.setTop(hn ? hn.el.getHeight() : 0);
27415         }
27416         this.adjustments = [
27417             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27418             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27419         ];
27420     }
27421
27422     if(this.draggable){
27423         this.dd = this.dynamic ?
27424             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27425         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27426     }
27427
27428     // public events
27429     this.addEvents({
27430         /**
27431          * @event beforeresize
27432          * Fired before resize is allowed. Set enabled to false to cancel resize.
27433          * @param {Roo.Resizable} this
27434          * @param {Roo.EventObject} e The mousedown event
27435          */
27436         "beforeresize" : true,
27437         /**
27438          * @event resize
27439          * Fired after a resize.
27440          * @param {Roo.Resizable} this
27441          * @param {Number} width The new width
27442          * @param {Number} height The new height
27443          * @param {Roo.EventObject} e The mouseup event
27444          */
27445         "resize" : true
27446     });
27447
27448     if(this.width !== null && this.height !== null){
27449         this.resizeTo(this.width, this.height);
27450     }else{
27451         this.updateChildSize();
27452     }
27453     if(Roo.isIE){
27454         this.el.dom.style.zoom = 1;
27455     }
27456     Roo.Resizable.superclass.constructor.call(this);
27457 };
27458
27459 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27460         resizeChild : false,
27461         adjustments : [0, 0],
27462         minWidth : 5,
27463         minHeight : 5,
27464         maxWidth : 10000,
27465         maxHeight : 10000,
27466         enabled : true,
27467         animate : false,
27468         duration : .35,
27469         dynamic : false,
27470         handles : false,
27471         multiDirectional : false,
27472         disableTrackOver : false,
27473         easing : 'easeOutStrong',
27474         widthIncrement : 0,
27475         heightIncrement : 0,
27476         pinned : false,
27477         width : null,
27478         height : null,
27479         preserveRatio : false,
27480         transparent: false,
27481         minX: 0,
27482         minY: 0,
27483         draggable: false,
27484
27485         /**
27486          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27487          */
27488         constrainTo: undefined,
27489         /**
27490          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27491          */
27492         resizeRegion: undefined,
27493
27494
27495     /**
27496      * Perform a manual resize
27497      * @param {Number} width
27498      * @param {Number} height
27499      */
27500     resizeTo : function(width, height){
27501         this.el.setSize(width, height);
27502         this.updateChildSize();
27503         this.fireEvent("resize", this, width, height, null);
27504     },
27505
27506     // private
27507     startSizing : function(e, handle){
27508         this.fireEvent("beforeresize", this, e);
27509         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27510
27511             if(!this.overlay){
27512                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27513                 this.overlay.unselectable();
27514                 this.overlay.enableDisplayMode("block");
27515                 this.overlay.on("mousemove", this.onMouseMove, this);
27516                 this.overlay.on("mouseup", this.onMouseUp, this);
27517             }
27518             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27519
27520             this.resizing = true;
27521             this.startBox = this.el.getBox();
27522             this.startPoint = e.getXY();
27523             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27524                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27525
27526             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27527             this.overlay.show();
27528
27529             if(this.constrainTo) {
27530                 var ct = Roo.get(this.constrainTo);
27531                 this.resizeRegion = ct.getRegion().adjust(
27532                     ct.getFrameWidth('t'),
27533                     ct.getFrameWidth('l'),
27534                     -ct.getFrameWidth('b'),
27535                     -ct.getFrameWidth('r')
27536                 );
27537             }
27538
27539             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27540             this.proxy.show();
27541             this.proxy.setBox(this.startBox);
27542             if(!this.dynamic){
27543                 this.proxy.setStyle('visibility', 'visible');
27544             }
27545         }
27546     },
27547
27548     // private
27549     onMouseDown : function(handle, e){
27550         if(this.enabled){
27551             e.stopEvent();
27552             this.activeHandle = handle;
27553             this.startSizing(e, handle);
27554         }
27555     },
27556
27557     // private
27558     onMouseUp : function(e){
27559         var size = this.resizeElement();
27560         this.resizing = false;
27561         this.handleOut();
27562         this.overlay.hide();
27563         this.proxy.hide();
27564         this.fireEvent("resize", this, size.width, size.height, e);
27565     },
27566
27567     // private
27568     updateChildSize : function(){
27569         if(this.resizeChild){
27570             var el = this.el;
27571             var child = this.resizeChild;
27572             var adj = this.adjustments;
27573             if(el.dom.offsetWidth){
27574                 var b = el.getSize(true);
27575                 child.setSize(b.width+adj[0], b.height+adj[1]);
27576             }
27577             // Second call here for IE
27578             // The first call enables instant resizing and
27579             // the second call corrects scroll bars if they
27580             // exist
27581             if(Roo.isIE){
27582                 setTimeout(function(){
27583                     if(el.dom.offsetWidth){
27584                         var b = el.getSize(true);
27585                         child.setSize(b.width+adj[0], b.height+adj[1]);
27586                     }
27587                 }, 10);
27588             }
27589         }
27590     },
27591
27592     // private
27593     snap : function(value, inc, min){
27594         if(!inc || !value) return value;
27595         var newValue = value;
27596         var m = value % inc;
27597         if(m > 0){
27598             if(m > (inc/2)){
27599                 newValue = value + (inc-m);
27600             }else{
27601                 newValue = value - m;
27602             }
27603         }
27604         return Math.max(min, newValue);
27605     },
27606
27607     // private
27608     resizeElement : function(){
27609         var box = this.proxy.getBox();
27610         if(this.updateBox){
27611             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27612         }else{
27613             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27614         }
27615         this.updateChildSize();
27616         if(!this.dynamic){
27617             this.proxy.hide();
27618         }
27619         return box;
27620     },
27621
27622     // private
27623     constrain : function(v, diff, m, mx){
27624         if(v - diff < m){
27625             diff = v - m;
27626         }else if(v - diff > mx){
27627             diff = mx - v;
27628         }
27629         return diff;
27630     },
27631
27632     // private
27633     onMouseMove : function(e){
27634         if(this.enabled){
27635             try{// try catch so if something goes wrong the user doesn't get hung
27636
27637             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27638                 return;
27639             }
27640
27641             //var curXY = this.startPoint;
27642             var curSize = this.curSize || this.startBox;
27643             var x = this.startBox.x, y = this.startBox.y;
27644             var ox = x, oy = y;
27645             var w = curSize.width, h = curSize.height;
27646             var ow = w, oh = h;
27647             var mw = this.minWidth, mh = this.minHeight;
27648             var mxw = this.maxWidth, mxh = this.maxHeight;
27649             var wi = this.widthIncrement;
27650             var hi = this.heightIncrement;
27651
27652             var eventXY = e.getXY();
27653             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27654             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27655
27656             var pos = this.activeHandle.position;
27657
27658             switch(pos){
27659                 case "east":
27660                     w += diffX;
27661                     w = Math.min(Math.max(mw, w), mxw);
27662                     break;
27663                 case "south":
27664                     h += diffY;
27665                     h = Math.min(Math.max(mh, h), mxh);
27666                     break;
27667                 case "southeast":
27668                     w += diffX;
27669                     h += diffY;
27670                     w = Math.min(Math.max(mw, w), mxw);
27671                     h = Math.min(Math.max(mh, h), mxh);
27672                     break;
27673                 case "north":
27674                     diffY = this.constrain(h, diffY, mh, mxh);
27675                     y += diffY;
27676                     h -= diffY;
27677                     break;
27678                 case "west":
27679                     diffX = this.constrain(w, diffX, mw, mxw);
27680                     x += diffX;
27681                     w -= diffX;
27682                     break;
27683                 case "northeast":
27684                     w += diffX;
27685                     w = Math.min(Math.max(mw, w), mxw);
27686                     diffY = this.constrain(h, diffY, mh, mxh);
27687                     y += diffY;
27688                     h -= diffY;
27689                     break;
27690                 case "northwest":
27691                     diffX = this.constrain(w, diffX, mw, mxw);
27692                     diffY = this.constrain(h, diffY, mh, mxh);
27693                     y += diffY;
27694                     h -= diffY;
27695                     x += diffX;
27696                     w -= diffX;
27697                     break;
27698                case "southwest":
27699                     diffX = this.constrain(w, diffX, mw, mxw);
27700                     h += diffY;
27701                     h = Math.min(Math.max(mh, h), mxh);
27702                     x += diffX;
27703                     w -= diffX;
27704                     break;
27705             }
27706
27707             var sw = this.snap(w, wi, mw);
27708             var sh = this.snap(h, hi, mh);
27709             if(sw != w || sh != h){
27710                 switch(pos){
27711                     case "northeast":
27712                         y -= sh - h;
27713                     break;
27714                     case "north":
27715                         y -= sh - h;
27716                         break;
27717                     case "southwest":
27718                         x -= sw - w;
27719                     break;
27720                     case "west":
27721                         x -= sw - w;
27722                         break;
27723                     case "northwest":
27724                         x -= sw - w;
27725                         y -= sh - h;
27726                     break;
27727                 }
27728                 w = sw;
27729                 h = sh;
27730             }
27731
27732             if(this.preserveRatio){
27733                 switch(pos){
27734                     case "southeast":
27735                     case "east":
27736                         h = oh * (w/ow);
27737                         h = Math.min(Math.max(mh, h), mxh);
27738                         w = ow * (h/oh);
27739                        break;
27740                     case "south":
27741                         w = ow * (h/oh);
27742                         w = Math.min(Math.max(mw, w), mxw);
27743                         h = oh * (w/ow);
27744                         break;
27745                     case "northeast":
27746                         w = ow * (h/oh);
27747                         w = Math.min(Math.max(mw, w), mxw);
27748                         h = oh * (w/ow);
27749                     break;
27750                     case "north":
27751                         var tw = w;
27752                         w = ow * (h/oh);
27753                         w = Math.min(Math.max(mw, w), mxw);
27754                         h = oh * (w/ow);
27755                         x += (tw - w) / 2;
27756                         break;
27757                     case "southwest":
27758                         h = oh * (w/ow);
27759                         h = Math.min(Math.max(mh, h), mxh);
27760                         var tw = w;
27761                         w = ow * (h/oh);
27762                         x += tw - w;
27763                         break;
27764                     case "west":
27765                         var th = h;
27766                         h = oh * (w/ow);
27767                         h = Math.min(Math.max(mh, h), mxh);
27768                         y += (th - h) / 2;
27769                         var tw = w;
27770                         w = ow * (h/oh);
27771                         x += tw - w;
27772                        break;
27773                     case "northwest":
27774                         var tw = w;
27775                         var th = h;
27776                         h = oh * (w/ow);
27777                         h = Math.min(Math.max(mh, h), mxh);
27778                         w = ow * (h/oh);
27779                         y += th - h;
27780                          x += tw - w;
27781                        break;
27782
27783                 }
27784             }
27785             this.proxy.setBounds(x, y, w, h);
27786             if(this.dynamic){
27787                 this.resizeElement();
27788             }
27789             }catch(e){}
27790         }
27791     },
27792
27793     // private
27794     handleOver : function(){
27795         if(this.enabled){
27796             this.el.addClass("x-resizable-over");
27797         }
27798     },
27799
27800     // private
27801     handleOut : function(){
27802         if(!this.resizing){
27803             this.el.removeClass("x-resizable-over");
27804         }
27805     },
27806
27807     /**
27808      * Returns the element this component is bound to.
27809      * @return {Roo.Element}
27810      */
27811     getEl : function(){
27812         return this.el;
27813     },
27814
27815     /**
27816      * Returns the resizeChild element (or null).
27817      * @return {Roo.Element}
27818      */
27819     getResizeChild : function(){
27820         return this.resizeChild;
27821     },
27822
27823     /**
27824      * Destroys this resizable. If the element was wrapped and
27825      * removeEl is not true then the element remains.
27826      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27827      */
27828     destroy : function(removeEl){
27829         this.proxy.remove();
27830         if(this.overlay){
27831             this.overlay.removeAllListeners();
27832             this.overlay.remove();
27833         }
27834         var ps = Roo.Resizable.positions;
27835         for(var k in ps){
27836             if(typeof ps[k] != "function" && this[ps[k]]){
27837                 var h = this[ps[k]];
27838                 h.el.removeAllListeners();
27839                 h.el.remove();
27840             }
27841         }
27842         if(removeEl){
27843             this.el.update("");
27844             this.el.remove();
27845         }
27846     }
27847 });
27848
27849 // private
27850 // hash to map config positions to true positions
27851 Roo.Resizable.positions = {
27852     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27853 };
27854
27855 // private
27856 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27857     if(!this.tpl){
27858         // only initialize the template if resizable is used
27859         var tpl = Roo.DomHelper.createTemplate(
27860             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27861         );
27862         tpl.compile();
27863         Roo.Resizable.Handle.prototype.tpl = tpl;
27864     }
27865     this.position = pos;
27866     this.rz = rz;
27867     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27868     this.el.unselectable();
27869     if(transparent){
27870         this.el.setOpacity(0);
27871     }
27872     this.el.on("mousedown", this.onMouseDown, this);
27873     if(!disableTrackOver){
27874         this.el.on("mouseover", this.onMouseOver, this);
27875         this.el.on("mouseout", this.onMouseOut, this);
27876     }
27877 };
27878
27879 // private
27880 Roo.Resizable.Handle.prototype = {
27881     afterResize : function(rz){
27882         // do nothing
27883     },
27884     // private
27885     onMouseDown : function(e){
27886         this.rz.onMouseDown(this, e);
27887     },
27888     // private
27889     onMouseOver : function(e){
27890         this.rz.handleOver(this, e);
27891     },
27892     // private
27893     onMouseOut : function(e){
27894         this.rz.handleOut(this, e);
27895     }
27896 };/*
27897  * Based on:
27898  * Ext JS Library 1.1.1
27899  * Copyright(c) 2006-2007, Ext JS, LLC.
27900  *
27901  * Originally Released Under LGPL - original licence link has changed is not relivant.
27902  *
27903  * Fork - LGPL
27904  * <script type="text/javascript">
27905  */
27906
27907 /**
27908  * @class Roo.Editor
27909  * @extends Roo.Component
27910  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27911  * @constructor
27912  * Create a new Editor
27913  * @param {Roo.form.Field} field The Field object (or descendant)
27914  * @param {Object} config The config object
27915  */
27916 Roo.Editor = function(field, config){
27917     Roo.Editor.superclass.constructor.call(this, config);
27918     this.field = field;
27919     this.addEvents({
27920         /**
27921              * @event beforestartedit
27922              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27923              * false from the handler of this event.
27924              * @param {Editor} this
27925              * @param {Roo.Element} boundEl The underlying element bound to this editor
27926              * @param {Mixed} value The field value being set
27927              */
27928         "beforestartedit" : true,
27929         /**
27930              * @event startedit
27931              * Fires when this editor is displayed
27932              * @param {Roo.Element} boundEl The underlying element bound to this editor
27933              * @param {Mixed} value The starting field value
27934              */
27935         "startedit" : true,
27936         /**
27937              * @event beforecomplete
27938              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27939              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27940              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27941              * event will not fire since no edit actually occurred.
27942              * @param {Editor} this
27943              * @param {Mixed} value The current field value
27944              * @param {Mixed} startValue The original field value
27945              */
27946         "beforecomplete" : true,
27947         /**
27948              * @event complete
27949              * Fires after editing is complete and any changed value has been written to the underlying field.
27950              * @param {Editor} this
27951              * @param {Mixed} value The current field value
27952              * @param {Mixed} startValue The original field value
27953              */
27954         "complete" : true,
27955         /**
27956          * @event specialkey
27957          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
27958          * {@link Roo.EventObject#getKey} to determine which key was pressed.
27959          * @param {Roo.form.Field} this
27960          * @param {Roo.EventObject} e The event object
27961          */
27962         "specialkey" : true
27963     });
27964 };
27965
27966 Roo.extend(Roo.Editor, Roo.Component, {
27967     /**
27968      * @cfg {Boolean/String} autosize
27969      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
27970      * or "height" to adopt the height only (defaults to false)
27971      */
27972     /**
27973      * @cfg {Boolean} revertInvalid
27974      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
27975      * validation fails (defaults to true)
27976      */
27977     /**
27978      * @cfg {Boolean} ignoreNoChange
27979      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
27980      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
27981      * will never be ignored.
27982      */
27983     /**
27984      * @cfg {Boolean} hideEl
27985      * False to keep the bound element visible while the editor is displayed (defaults to true)
27986      */
27987     /**
27988      * @cfg {Mixed} value
27989      * The data value of the underlying field (defaults to "")
27990      */
27991     value : "",
27992     /**
27993      * @cfg {String} alignment
27994      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
27995      */
27996     alignment: "c-c?",
27997     /**
27998      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
27999      * for bottom-right shadow (defaults to "frame")
28000      */
28001     shadow : "frame",
28002     /**
28003      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28004      */
28005     constrain : false,
28006     /**
28007      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28008      */
28009     completeOnEnter : false,
28010     /**
28011      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28012      */
28013     cancelOnEsc : false,
28014     /**
28015      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28016      */
28017     updateEl : false,
28018
28019     // private
28020     onRender : function(ct, position){
28021         this.el = new Roo.Layer({
28022             shadow: this.shadow,
28023             cls: "x-editor",
28024             parentEl : ct,
28025             shim : this.shim,
28026             shadowOffset:4,
28027             id: this.id,
28028             constrain: this.constrain
28029         });
28030         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28031         if(this.field.msgTarget != 'title'){
28032             this.field.msgTarget = 'qtip';
28033         }
28034         this.field.render(this.el);
28035         if(Roo.isGecko){
28036             this.field.el.dom.setAttribute('autocomplete', 'off');
28037         }
28038         this.field.on("specialkey", this.onSpecialKey, this);
28039         if(this.swallowKeys){
28040             this.field.el.swallowEvent(['keydown','keypress']);
28041         }
28042         this.field.show();
28043         this.field.on("blur", this.onBlur, this);
28044         if(this.field.grow){
28045             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28046         }
28047     },
28048
28049     onSpecialKey : function(field, e){
28050         if(this.completeOnEnter && e.getKey() == e.ENTER){
28051             e.stopEvent();
28052             this.completeEdit();
28053         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28054             this.cancelEdit();
28055         }else{
28056             this.fireEvent('specialkey', field, e);
28057         }
28058     },
28059
28060     /**
28061      * Starts the editing process and shows the editor.
28062      * @param {String/HTMLElement/Element} el The element to edit
28063      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28064       * to the innerHTML of el.
28065      */
28066     startEdit : function(el, value){
28067         if(this.editing){
28068             this.completeEdit();
28069         }
28070         this.boundEl = Roo.get(el);
28071         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28072         if(!this.rendered){
28073             this.render(this.parentEl || document.body);
28074         }
28075         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28076             return;
28077         }
28078         this.startValue = v;
28079         this.field.setValue(v);
28080         if(this.autoSize){
28081             var sz = this.boundEl.getSize();
28082             switch(this.autoSize){
28083                 case "width":
28084                 this.setSize(sz.width,  "");
28085                 break;
28086                 case "height":
28087                 this.setSize("",  sz.height);
28088                 break;
28089                 default:
28090                 this.setSize(sz.width,  sz.height);
28091             }
28092         }
28093         this.el.alignTo(this.boundEl, this.alignment);
28094         this.editing = true;
28095         if(Roo.QuickTips){
28096             Roo.QuickTips.disable();
28097         }
28098         this.show();
28099     },
28100
28101     /**
28102      * Sets the height and width of this editor.
28103      * @param {Number} width The new width
28104      * @param {Number} height The new height
28105      */
28106     setSize : function(w, h){
28107         this.field.setSize(w, h);
28108         if(this.el){
28109             this.el.sync();
28110         }
28111     },
28112
28113     /**
28114      * Realigns the editor to the bound field based on the current alignment config value.
28115      */
28116     realign : function(){
28117         this.el.alignTo(this.boundEl, this.alignment);
28118     },
28119
28120     /**
28121      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28122      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28123      */
28124     completeEdit : function(remainVisible){
28125         if(!this.editing){
28126             return;
28127         }
28128         var v = this.getValue();
28129         if(this.revertInvalid !== false && !this.field.isValid()){
28130             v = this.startValue;
28131             this.cancelEdit(true);
28132         }
28133         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28134             this.editing = false;
28135             this.hide();
28136             return;
28137         }
28138         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28139             this.editing = false;
28140             if(this.updateEl && this.boundEl){
28141                 this.boundEl.update(v);
28142             }
28143             if(remainVisible !== true){
28144                 this.hide();
28145             }
28146             this.fireEvent("complete", this, v, this.startValue);
28147         }
28148     },
28149
28150     // private
28151     onShow : function(){
28152         this.el.show();
28153         if(this.hideEl !== false){
28154             this.boundEl.hide();
28155         }
28156         this.field.show();
28157         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28158             this.fixIEFocus = true;
28159             this.deferredFocus.defer(50, this);
28160         }else{
28161             this.field.focus();
28162         }
28163         this.fireEvent("startedit", this.boundEl, this.startValue);
28164     },
28165
28166     deferredFocus : function(){
28167         if(this.editing){
28168             this.field.focus();
28169         }
28170     },
28171
28172     /**
28173      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28174      * reverted to the original starting value.
28175      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28176      * cancel (defaults to false)
28177      */
28178     cancelEdit : function(remainVisible){
28179         if(this.editing){
28180             this.setValue(this.startValue);
28181             if(remainVisible !== true){
28182                 this.hide();
28183             }
28184         }
28185     },
28186
28187     // private
28188     onBlur : function(){
28189         if(this.allowBlur !== true && this.editing){
28190             this.completeEdit();
28191         }
28192     },
28193
28194     // private
28195     onHide : function(){
28196         if(this.editing){
28197             this.completeEdit();
28198             return;
28199         }
28200         this.field.blur();
28201         if(this.field.collapse){
28202             this.field.collapse();
28203         }
28204         this.el.hide();
28205         if(this.hideEl !== false){
28206             this.boundEl.show();
28207         }
28208         if(Roo.QuickTips){
28209             Roo.QuickTips.enable();
28210         }
28211     },
28212
28213     /**
28214      * Sets the data value of the editor
28215      * @param {Mixed} value Any valid value supported by the underlying field
28216      */
28217     setValue : function(v){
28218         this.field.setValue(v);
28219     },
28220
28221     /**
28222      * Gets the data value of the editor
28223      * @return {Mixed} The data value
28224      */
28225     getValue : function(){
28226         return this.field.getValue();
28227     }
28228 });/*
28229  * Based on:
28230  * Ext JS Library 1.1.1
28231  * Copyright(c) 2006-2007, Ext JS, LLC.
28232  *
28233  * Originally Released Under LGPL - original licence link has changed is not relivant.
28234  *
28235  * Fork - LGPL
28236  * <script type="text/javascript">
28237  */
28238  
28239 /**
28240  * @class Roo.BasicDialog
28241  * @extends Roo.util.Observable
28242  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28243  * <pre><code>
28244 var dlg = new Roo.BasicDialog("my-dlg", {
28245     height: 200,
28246     width: 300,
28247     minHeight: 100,
28248     minWidth: 150,
28249     modal: true,
28250     proxyDrag: true,
28251     shadow: true
28252 });
28253 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28254 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28255 dlg.addButton('Cancel', dlg.hide, dlg);
28256 dlg.show();
28257 </code></pre>
28258   <b>A Dialog should always be a direct child of the body element.</b>
28259  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28260  * @cfg {String} title Default text to display in the title bar (defaults to null)
28261  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28262  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28263  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28264  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28265  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28266  * (defaults to null with no animation)
28267  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28268  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28269  * property for valid values (defaults to 'all')
28270  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28271  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28272  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28273  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28274  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28275  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28276  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28277  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28278  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28279  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28280  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28281  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28282  * draggable = true (defaults to false)
28283  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28284  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28285  * shadow (defaults to false)
28286  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28287  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28288  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28289  * @cfg {Array} buttons Array of buttons
28290  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28291  * @constructor
28292  * Create a new BasicDialog.
28293  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28294  * @param {Object} config Configuration options
28295  */
28296 Roo.BasicDialog = function(el, config){
28297     this.el = Roo.get(el);
28298     var dh = Roo.DomHelper;
28299     if(!this.el && config && config.autoCreate){
28300         if(typeof config.autoCreate == "object"){
28301             if(!config.autoCreate.id){
28302                 config.autoCreate.id = el;
28303             }
28304             this.el = dh.append(document.body,
28305                         config.autoCreate, true);
28306         }else{
28307             this.el = dh.append(document.body,
28308                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28309         }
28310     }
28311     el = this.el;
28312     el.setDisplayed(true);
28313     el.hide = this.hideAction;
28314     this.id = el.id;
28315     el.addClass("x-dlg");
28316
28317     Roo.apply(this, config);
28318
28319     this.proxy = el.createProxy("x-dlg-proxy");
28320     this.proxy.hide = this.hideAction;
28321     this.proxy.setOpacity(.5);
28322     this.proxy.hide();
28323
28324     if(config.width){
28325         el.setWidth(config.width);
28326     }
28327     if(config.height){
28328         el.setHeight(config.height);
28329     }
28330     this.size = el.getSize();
28331     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28332         this.xy = [config.x,config.y];
28333     }else{
28334         this.xy = el.getCenterXY(true);
28335     }
28336     /** The header element @type Roo.Element */
28337     this.header = el.child("> .x-dlg-hd");
28338     /** The body element @type Roo.Element */
28339     this.body = el.child("> .x-dlg-bd");
28340     /** The footer element @type Roo.Element */
28341     this.footer = el.child("> .x-dlg-ft");
28342
28343     if(!this.header){
28344         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28345     }
28346     if(!this.body){
28347         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28348     }
28349
28350     this.header.unselectable();
28351     if(this.title){
28352         this.header.update(this.title);
28353     }
28354     // this element allows the dialog to be focused for keyboard event
28355     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28356     this.focusEl.swallowEvent("click", true);
28357
28358     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28359
28360     // wrap the body and footer for special rendering
28361     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28362     if(this.footer){
28363         this.bwrap.dom.appendChild(this.footer.dom);
28364     }
28365
28366     this.bg = this.el.createChild({
28367         tag: "div", cls:"x-dlg-bg",
28368         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28369     });
28370     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28371
28372
28373     if(this.autoScroll !== false && !this.autoTabs){
28374         this.body.setStyle("overflow", "auto");
28375     }
28376
28377     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28378
28379     if(this.closable !== false){
28380         this.el.addClass("x-dlg-closable");
28381         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28382         this.close.on("click", this.closeClick, this);
28383         this.close.addClassOnOver("x-dlg-close-over");
28384     }
28385     if(this.collapsible !== false){
28386         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28387         this.collapseBtn.on("click", this.collapseClick, this);
28388         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28389         this.header.on("dblclick", this.collapseClick, this);
28390     }
28391     if(this.resizable !== false){
28392         this.el.addClass("x-dlg-resizable");
28393         this.resizer = new Roo.Resizable(el, {
28394             minWidth: this.minWidth || 80,
28395             minHeight:this.minHeight || 80,
28396             handles: this.resizeHandles || "all",
28397             pinned: true
28398         });
28399         this.resizer.on("beforeresize", this.beforeResize, this);
28400         this.resizer.on("resize", this.onResize, this);
28401     }
28402     if(this.draggable !== false){
28403         el.addClass("x-dlg-draggable");
28404         if (!this.proxyDrag) {
28405             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28406         }
28407         else {
28408             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28409         }
28410         dd.setHandleElId(this.header.id);
28411         dd.endDrag = this.endMove.createDelegate(this);
28412         dd.startDrag = this.startMove.createDelegate(this);
28413         dd.onDrag = this.onDrag.createDelegate(this);
28414         dd.scroll = false;
28415         this.dd = dd;
28416     }
28417     if(this.modal){
28418         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28419         this.mask.enableDisplayMode("block");
28420         this.mask.hide();
28421         this.el.addClass("x-dlg-modal");
28422     }
28423     if(this.shadow){
28424         this.shadow = new Roo.Shadow({
28425             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28426             offset : this.shadowOffset
28427         });
28428     }else{
28429         this.shadowOffset = 0;
28430     }
28431     if(Roo.useShims && this.shim !== false){
28432         this.shim = this.el.createShim();
28433         this.shim.hide = this.hideAction;
28434         this.shim.hide();
28435     }else{
28436         this.shim = false;
28437     }
28438     if(this.autoTabs){
28439         this.initTabs();
28440     }
28441     if (this.buttons) { 
28442         var bts= this.buttons;
28443         this.buttons = [];
28444         Roo.each(bts, function(b) {
28445             this.addButton(b);
28446         }, this);
28447     }
28448     
28449     
28450     this.addEvents({
28451         /**
28452          * @event keydown
28453          * Fires when a key is pressed
28454          * @param {Roo.BasicDialog} this
28455          * @param {Roo.EventObject} e
28456          */
28457         "keydown" : true,
28458         /**
28459          * @event move
28460          * Fires when this dialog is moved by the user.
28461          * @param {Roo.BasicDialog} this
28462          * @param {Number} x The new page X
28463          * @param {Number} y The new page Y
28464          */
28465         "move" : true,
28466         /**
28467          * @event resize
28468          * Fires when this dialog is resized by the user.
28469          * @param {Roo.BasicDialog} this
28470          * @param {Number} width The new width
28471          * @param {Number} height The new height
28472          */
28473         "resize" : true,
28474         /**
28475          * @event beforehide
28476          * Fires before this dialog is hidden.
28477          * @param {Roo.BasicDialog} this
28478          */
28479         "beforehide" : true,
28480         /**
28481          * @event hide
28482          * Fires when this dialog is hidden.
28483          * @param {Roo.BasicDialog} this
28484          */
28485         "hide" : true,
28486         /**
28487          * @event beforeshow
28488          * Fires before this dialog is shown.
28489          * @param {Roo.BasicDialog} this
28490          */
28491         "beforeshow" : true,
28492         /**
28493          * @event show
28494          * Fires when this dialog is shown.
28495          * @param {Roo.BasicDialog} this
28496          */
28497         "show" : true
28498     });
28499     el.on("keydown", this.onKeyDown, this);
28500     el.on("mousedown", this.toFront, this);
28501     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28502     this.el.hide();
28503     Roo.DialogManager.register(this);
28504     Roo.BasicDialog.superclass.constructor.call(this);
28505 };
28506
28507 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28508     shadowOffset: Roo.isIE ? 6 : 5,
28509     minHeight: 80,
28510     minWidth: 200,
28511     minButtonWidth: 75,
28512     defaultButton: null,
28513     buttonAlign: "right",
28514     tabTag: 'div',
28515     firstShow: true,
28516
28517     /**
28518      * Sets the dialog title text
28519      * @param {String} text The title text to display
28520      * @return {Roo.BasicDialog} this
28521      */
28522     setTitle : function(text){
28523         this.header.update(text);
28524         return this;
28525     },
28526
28527     // private
28528     closeClick : function(){
28529         this.hide();
28530     },
28531
28532     // private
28533     collapseClick : function(){
28534         this[this.collapsed ? "expand" : "collapse"]();
28535     },
28536
28537     /**
28538      * Collapses the dialog to its minimized state (only the title bar is visible).
28539      * Equivalent to the user clicking the collapse dialog button.
28540      */
28541     collapse : function(){
28542         if(!this.collapsed){
28543             this.collapsed = true;
28544             this.el.addClass("x-dlg-collapsed");
28545             this.restoreHeight = this.el.getHeight();
28546             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28547         }
28548     },
28549
28550     /**
28551      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28552      * clicking the expand dialog button.
28553      */
28554     expand : function(){
28555         if(this.collapsed){
28556             this.collapsed = false;
28557             this.el.removeClass("x-dlg-collapsed");
28558             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28559         }
28560     },
28561
28562     /**
28563      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28564      * @return {Roo.TabPanel} The tabs component
28565      */
28566     initTabs : function(){
28567         var tabs = this.getTabs();
28568         while(tabs.getTab(0)){
28569             tabs.removeTab(0);
28570         }
28571         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28572             var dom = el.dom;
28573             tabs.addTab(Roo.id(dom), dom.title);
28574             dom.title = "";
28575         });
28576         tabs.activate(0);
28577         return tabs;
28578     },
28579
28580     // private
28581     beforeResize : function(){
28582         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28583     },
28584
28585     // private
28586     onResize : function(){
28587         this.refreshSize();
28588         this.syncBodyHeight();
28589         this.adjustAssets();
28590         this.focus();
28591         this.fireEvent("resize", this, this.size.width, this.size.height);
28592     },
28593
28594     // private
28595     onKeyDown : function(e){
28596         if(this.isVisible()){
28597             this.fireEvent("keydown", this, e);
28598         }
28599     },
28600
28601     /**
28602      * Resizes the dialog.
28603      * @param {Number} width
28604      * @param {Number} height
28605      * @return {Roo.BasicDialog} this
28606      */
28607     resizeTo : function(width, height){
28608         this.el.setSize(width, height);
28609         this.size = {width: width, height: height};
28610         this.syncBodyHeight();
28611         if(this.fixedcenter){
28612             this.center();
28613         }
28614         if(this.isVisible()){
28615             this.constrainXY();
28616             this.adjustAssets();
28617         }
28618         this.fireEvent("resize", this, width, height);
28619         return this;
28620     },
28621
28622
28623     /**
28624      * Resizes the dialog to fit the specified content size.
28625      * @param {Number} width
28626      * @param {Number} height
28627      * @return {Roo.BasicDialog} this
28628      */
28629     setContentSize : function(w, h){
28630         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28631         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28632         //if(!this.el.isBorderBox()){
28633             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28634             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28635         //}
28636         if(this.tabs){
28637             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28638             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28639         }
28640         this.resizeTo(w, h);
28641         return this;
28642     },
28643
28644     /**
28645      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28646      * executed in response to a particular key being pressed while the dialog is active.
28647      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28648      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28649      * @param {Function} fn The function to call
28650      * @param {Object} scope (optional) The scope of the function
28651      * @return {Roo.BasicDialog} this
28652      */
28653     addKeyListener : function(key, fn, scope){
28654         var keyCode, shift, ctrl, alt;
28655         if(typeof key == "object" && !(key instanceof Array)){
28656             keyCode = key["key"];
28657             shift = key["shift"];
28658             ctrl = key["ctrl"];
28659             alt = key["alt"];
28660         }else{
28661             keyCode = key;
28662         }
28663         var handler = function(dlg, e){
28664             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28665                 var k = e.getKey();
28666                 if(keyCode instanceof Array){
28667                     for(var i = 0, len = keyCode.length; i < len; i++){
28668                         if(keyCode[i] == k){
28669                           fn.call(scope || window, dlg, k, e);
28670                           return;
28671                         }
28672                     }
28673                 }else{
28674                     if(k == keyCode){
28675                         fn.call(scope || window, dlg, k, e);
28676                     }
28677                 }
28678             }
28679         };
28680         this.on("keydown", handler);
28681         return this;
28682     },
28683
28684     /**
28685      * Returns the TabPanel component (creates it if it doesn't exist).
28686      * Note: If you wish to simply check for the existence of tabs without creating them,
28687      * check for a null 'tabs' property.
28688      * @return {Roo.TabPanel} The tabs component
28689      */
28690     getTabs : function(){
28691         if(!this.tabs){
28692             this.el.addClass("x-dlg-auto-tabs");
28693             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28694             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28695         }
28696         return this.tabs;
28697     },
28698
28699     /**
28700      * Adds a button to the footer section of the dialog.
28701      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28702      * object or a valid Roo.DomHelper element config
28703      * @param {Function} handler The function called when the button is clicked
28704      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28705      * @return {Roo.Button} The new button
28706      */
28707     addButton : function(config, handler, scope){
28708         var dh = Roo.DomHelper;
28709         if(!this.footer){
28710             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28711         }
28712         if(!this.btnContainer){
28713             var tb = this.footer.createChild({
28714
28715                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28716                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28717             }, null, true);
28718             this.btnContainer = tb.firstChild.firstChild.firstChild;
28719         }
28720         var bconfig = {
28721             handler: handler,
28722             scope: scope,
28723             minWidth: this.minButtonWidth,
28724             hideParent:true
28725         };
28726         if(typeof config == "string"){
28727             bconfig.text = config;
28728         }else{
28729             if(config.tag){
28730                 bconfig.dhconfig = config;
28731             }else{
28732                 Roo.apply(bconfig, config);
28733             }
28734         }
28735         var fc = false;
28736         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28737             bconfig.position = Math.max(0, bconfig.position);
28738             fc = this.btnContainer.childNodes[bconfig.position];
28739         }
28740          
28741         var btn = new Roo.Button(
28742             fc ? 
28743                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28744                 : this.btnContainer.appendChild(document.createElement("td")),
28745             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28746             bconfig
28747         );
28748         this.syncBodyHeight();
28749         if(!this.buttons){
28750             /**
28751              * Array of all the buttons that have been added to this dialog via addButton
28752              * @type Array
28753              */
28754             this.buttons = [];
28755         }
28756         this.buttons.push(btn);
28757         return btn;
28758     },
28759
28760     /**
28761      * Sets the default button to be focused when the dialog is displayed.
28762      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28763      * @return {Roo.BasicDialog} this
28764      */
28765     setDefaultButton : function(btn){
28766         this.defaultButton = btn;
28767         return this;
28768     },
28769
28770     // private
28771     getHeaderFooterHeight : function(safe){
28772         var height = 0;
28773         if(this.header){
28774            height += this.header.getHeight();
28775         }
28776         if(this.footer){
28777            var fm = this.footer.getMargins();
28778             height += (this.footer.getHeight()+fm.top+fm.bottom);
28779         }
28780         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28781         height += this.centerBg.getPadding("tb");
28782         return height;
28783     },
28784
28785     // private
28786     syncBodyHeight : function(){
28787         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28788         var height = this.size.height - this.getHeaderFooterHeight(false);
28789         bd.setHeight(height-bd.getMargins("tb"));
28790         var hh = this.header.getHeight();
28791         var h = this.size.height-hh;
28792         cb.setHeight(h);
28793         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28794         bw.setHeight(h-cb.getPadding("tb"));
28795         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28796         bd.setWidth(bw.getWidth(true));
28797         if(this.tabs){
28798             this.tabs.syncHeight();
28799             if(Roo.isIE){
28800                 this.tabs.el.repaint();
28801             }
28802         }
28803     },
28804
28805     /**
28806      * Restores the previous state of the dialog if Roo.state is configured.
28807      * @return {Roo.BasicDialog} this
28808      */
28809     restoreState : function(){
28810         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28811         if(box && box.width){
28812             this.xy = [box.x, box.y];
28813             this.resizeTo(box.width, box.height);
28814         }
28815         return this;
28816     },
28817
28818     // private
28819     beforeShow : function(){
28820         this.expand();
28821         if(this.fixedcenter){
28822             this.xy = this.el.getCenterXY(true);
28823         }
28824         if(this.modal){
28825             Roo.get(document.body).addClass("x-body-masked");
28826             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28827             this.mask.show();
28828         }
28829         this.constrainXY();
28830     },
28831
28832     // private
28833     animShow : function(){
28834         var b = Roo.get(this.animateTarget, true).getBox();
28835         this.proxy.setSize(b.width, b.height);
28836         this.proxy.setLocation(b.x, b.y);
28837         this.proxy.show();
28838         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28839                     true, .35, this.showEl.createDelegate(this));
28840     },
28841
28842     /**
28843      * Shows the dialog.
28844      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28845      * @return {Roo.BasicDialog} this
28846      */
28847     show : function(animateTarget){
28848         if (this.fireEvent("beforeshow", this) === false){
28849             return;
28850         }
28851         if(this.syncHeightBeforeShow){
28852             this.syncBodyHeight();
28853         }else if(this.firstShow){
28854             this.firstShow = false;
28855             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28856         }
28857         this.animateTarget = animateTarget || this.animateTarget;
28858         if(!this.el.isVisible()){
28859             this.beforeShow();
28860             if(this.animateTarget){
28861                 this.animShow();
28862             }else{
28863                 this.showEl();
28864             }
28865         }
28866         return this;
28867     },
28868
28869     // private
28870     showEl : function(){
28871         this.proxy.hide();
28872         this.el.setXY(this.xy);
28873         this.el.show();
28874         this.adjustAssets(true);
28875         this.toFront();
28876         this.focus();
28877         // IE peekaboo bug - fix found by Dave Fenwick
28878         if(Roo.isIE){
28879             this.el.repaint();
28880         }
28881         this.fireEvent("show", this);
28882     },
28883
28884     /**
28885      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28886      * dialog itself will receive focus.
28887      */
28888     focus : function(){
28889         if(this.defaultButton){
28890             this.defaultButton.focus();
28891         }else{
28892             this.focusEl.focus();
28893         }
28894     },
28895
28896     // private
28897     constrainXY : function(){
28898         if(this.constraintoviewport !== false){
28899             if(!this.viewSize){
28900                 if(this.container){
28901                     var s = this.container.getSize();
28902                     this.viewSize = [s.width, s.height];
28903                 }else{
28904                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28905                 }
28906             }
28907             var s = Roo.get(this.container||document).getScroll();
28908
28909             var x = this.xy[0], y = this.xy[1];
28910             var w = this.size.width, h = this.size.height;
28911             var vw = this.viewSize[0], vh = this.viewSize[1];
28912             // only move it if it needs it
28913             var moved = false;
28914             // first validate right/bottom
28915             if(x + w > vw+s.left){
28916                 x = vw - w;
28917                 moved = true;
28918             }
28919             if(y + h > vh+s.top){
28920                 y = vh - h;
28921                 moved = true;
28922             }
28923             // then make sure top/left isn't negative
28924             if(x < s.left){
28925                 x = s.left;
28926                 moved = true;
28927             }
28928             if(y < s.top){
28929                 y = s.top;
28930                 moved = true;
28931             }
28932             if(moved){
28933                 // cache xy
28934                 this.xy = [x, y];
28935                 if(this.isVisible()){
28936                     this.el.setLocation(x, y);
28937                     this.adjustAssets();
28938                 }
28939             }
28940         }
28941     },
28942
28943     // private
28944     onDrag : function(){
28945         if(!this.proxyDrag){
28946             this.xy = this.el.getXY();
28947             this.adjustAssets();
28948         }
28949     },
28950
28951     // private
28952     adjustAssets : function(doShow){
28953         var x = this.xy[0], y = this.xy[1];
28954         var w = this.size.width, h = this.size.height;
28955         if(doShow === true){
28956             if(this.shadow){
28957                 this.shadow.show(this.el);
28958             }
28959             if(this.shim){
28960                 this.shim.show();
28961             }
28962         }
28963         if(this.shadow && this.shadow.isVisible()){
28964             this.shadow.show(this.el);
28965         }
28966         if(this.shim && this.shim.isVisible()){
28967             this.shim.setBounds(x, y, w, h);
28968         }
28969     },
28970
28971     // private
28972     adjustViewport : function(w, h){
28973         if(!w || !h){
28974             w = Roo.lib.Dom.getViewWidth();
28975             h = Roo.lib.Dom.getViewHeight();
28976         }
28977         // cache the size
28978         this.viewSize = [w, h];
28979         if(this.modal && this.mask.isVisible()){
28980             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
28981             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28982         }
28983         if(this.isVisible()){
28984             this.constrainXY();
28985         }
28986     },
28987
28988     /**
28989      * Destroys this dialog and all its supporting elements (including any tabs, shim,
28990      * shadow, proxy, mask, etc.)  Also removes all event listeners.
28991      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28992      */
28993     destroy : function(removeEl){
28994         if(this.isVisible()){
28995             this.animateTarget = null;
28996             this.hide();
28997         }
28998         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
28999         if(this.tabs){
29000             this.tabs.destroy(removeEl);
29001         }
29002         Roo.destroy(
29003              this.shim,
29004              this.proxy,
29005              this.resizer,
29006              this.close,
29007              this.mask
29008         );
29009         if(this.dd){
29010             this.dd.unreg();
29011         }
29012         if(this.buttons){
29013            for(var i = 0, len = this.buttons.length; i < len; i++){
29014                this.buttons[i].destroy();
29015            }
29016         }
29017         this.el.removeAllListeners();
29018         if(removeEl === true){
29019             this.el.update("");
29020             this.el.remove();
29021         }
29022         Roo.DialogManager.unregister(this);
29023     },
29024
29025     // private
29026     startMove : function(){
29027         if(this.proxyDrag){
29028             this.proxy.show();
29029         }
29030         if(this.constraintoviewport !== false){
29031             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29032         }
29033     },
29034
29035     // private
29036     endMove : function(){
29037         if(!this.proxyDrag){
29038             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29039         }else{
29040             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29041             this.proxy.hide();
29042         }
29043         this.refreshSize();
29044         this.adjustAssets();
29045         this.focus();
29046         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29047     },
29048
29049     /**
29050      * Brings this dialog to the front of any other visible dialogs
29051      * @return {Roo.BasicDialog} this
29052      */
29053     toFront : function(){
29054         Roo.DialogManager.bringToFront(this);
29055         return this;
29056     },
29057
29058     /**
29059      * Sends this dialog to the back (under) of any other visible dialogs
29060      * @return {Roo.BasicDialog} this
29061      */
29062     toBack : function(){
29063         Roo.DialogManager.sendToBack(this);
29064         return this;
29065     },
29066
29067     /**
29068      * Centers this dialog in the viewport
29069      * @return {Roo.BasicDialog} this
29070      */
29071     center : function(){
29072         var xy = this.el.getCenterXY(true);
29073         this.moveTo(xy[0], xy[1]);
29074         return this;
29075     },
29076
29077     /**
29078      * Moves the dialog's top-left corner to the specified point
29079      * @param {Number} x
29080      * @param {Number} y
29081      * @return {Roo.BasicDialog} this
29082      */
29083     moveTo : function(x, y){
29084         this.xy = [x,y];
29085         if(this.isVisible()){
29086             this.el.setXY(this.xy);
29087             this.adjustAssets();
29088         }
29089         return this;
29090     },
29091
29092     /**
29093      * Aligns the dialog to the specified element
29094      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29095      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29096      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29097      * @return {Roo.BasicDialog} this
29098      */
29099     alignTo : function(element, position, offsets){
29100         this.xy = this.el.getAlignToXY(element, position, offsets);
29101         if(this.isVisible()){
29102             this.el.setXY(this.xy);
29103             this.adjustAssets();
29104         }
29105         return this;
29106     },
29107
29108     /**
29109      * Anchors an element to another element and realigns it when the window is resized.
29110      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29111      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29112      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29113      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29114      * is a number, it is used as the buffer delay (defaults to 50ms).
29115      * @return {Roo.BasicDialog} this
29116      */
29117     anchorTo : function(el, alignment, offsets, monitorScroll){
29118         var action = function(){
29119             this.alignTo(el, alignment, offsets);
29120         };
29121         Roo.EventManager.onWindowResize(action, this);
29122         var tm = typeof monitorScroll;
29123         if(tm != 'undefined'){
29124             Roo.EventManager.on(window, 'scroll', action, this,
29125                 {buffer: tm == 'number' ? monitorScroll : 50});
29126         }
29127         action.call(this);
29128         return this;
29129     },
29130
29131     /**
29132      * Returns true if the dialog is visible
29133      * @return {Boolean}
29134      */
29135     isVisible : function(){
29136         return this.el.isVisible();
29137     },
29138
29139     // private
29140     animHide : function(callback){
29141         var b = Roo.get(this.animateTarget).getBox();
29142         this.proxy.show();
29143         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29144         this.el.hide();
29145         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29146                     this.hideEl.createDelegate(this, [callback]));
29147     },
29148
29149     /**
29150      * Hides the dialog.
29151      * @param {Function} callback (optional) Function to call when the dialog is hidden
29152      * @return {Roo.BasicDialog} this
29153      */
29154     hide : function(callback){
29155         if (this.fireEvent("beforehide", this) === false){
29156             return;
29157         }
29158         if(this.shadow){
29159             this.shadow.hide();
29160         }
29161         if(this.shim) {
29162           this.shim.hide();
29163         }
29164         if(this.animateTarget){
29165            this.animHide(callback);
29166         }else{
29167             this.el.hide();
29168             this.hideEl(callback);
29169         }
29170         return this;
29171     },
29172
29173     // private
29174     hideEl : function(callback){
29175         this.proxy.hide();
29176         if(this.modal){
29177             this.mask.hide();
29178             Roo.get(document.body).removeClass("x-body-masked");
29179         }
29180         this.fireEvent("hide", this);
29181         if(typeof callback == "function"){
29182             callback();
29183         }
29184     },
29185
29186     // private
29187     hideAction : function(){
29188         this.setLeft("-10000px");
29189         this.setTop("-10000px");
29190         this.setStyle("visibility", "hidden");
29191     },
29192
29193     // private
29194     refreshSize : function(){
29195         this.size = this.el.getSize();
29196         this.xy = this.el.getXY();
29197         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29198     },
29199
29200     // private
29201     // z-index is managed by the DialogManager and may be overwritten at any time
29202     setZIndex : function(index){
29203         if(this.modal){
29204             this.mask.setStyle("z-index", index);
29205         }
29206         if(this.shim){
29207             this.shim.setStyle("z-index", ++index);
29208         }
29209         if(this.shadow){
29210             this.shadow.setZIndex(++index);
29211         }
29212         this.el.setStyle("z-index", ++index);
29213         if(this.proxy){
29214             this.proxy.setStyle("z-index", ++index);
29215         }
29216         if(this.resizer){
29217             this.resizer.proxy.setStyle("z-index", ++index);
29218         }
29219
29220         this.lastZIndex = index;
29221     },
29222
29223     /**
29224      * Returns the element for this dialog
29225      * @return {Roo.Element} The underlying dialog Element
29226      */
29227     getEl : function(){
29228         return this.el;
29229     }
29230 });
29231
29232 /**
29233  * @class Roo.DialogManager
29234  * Provides global access to BasicDialogs that have been created and
29235  * support for z-indexing (layering) multiple open dialogs.
29236  */
29237 Roo.DialogManager = function(){
29238     var list = {};
29239     var accessList = [];
29240     var front = null;
29241
29242     // private
29243     var sortDialogs = function(d1, d2){
29244         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29245     };
29246
29247     // private
29248     var orderDialogs = function(){
29249         accessList.sort(sortDialogs);
29250         var seed = Roo.DialogManager.zseed;
29251         for(var i = 0, len = accessList.length; i < len; i++){
29252             var dlg = accessList[i];
29253             if(dlg){
29254                 dlg.setZIndex(seed + (i*10));
29255             }
29256         }
29257     };
29258
29259     return {
29260         /**
29261          * The starting z-index for BasicDialogs (defaults to 9000)
29262          * @type Number The z-index value
29263          */
29264         zseed : 9000,
29265
29266         // private
29267         register : function(dlg){
29268             list[dlg.id] = dlg;
29269             accessList.push(dlg);
29270         },
29271
29272         // private
29273         unregister : function(dlg){
29274             delete list[dlg.id];
29275             var i=0;
29276             var len=0;
29277             if(!accessList.indexOf){
29278                 for(  i = 0, len = accessList.length; i < len; i++){
29279                     if(accessList[i] == dlg){
29280                         accessList.splice(i, 1);
29281                         return;
29282                     }
29283                 }
29284             }else{
29285                  i = accessList.indexOf(dlg);
29286                 if(i != -1){
29287                     accessList.splice(i, 1);
29288                 }
29289             }
29290         },
29291
29292         /**
29293          * Gets a registered dialog by id
29294          * @param {String/Object} id The id of the dialog or a dialog
29295          * @return {Roo.BasicDialog} this
29296          */
29297         get : function(id){
29298             return typeof id == "object" ? id : list[id];
29299         },
29300
29301         /**
29302          * Brings the specified dialog to the front
29303          * @param {String/Object} dlg The id of the dialog or a dialog
29304          * @return {Roo.BasicDialog} this
29305          */
29306         bringToFront : function(dlg){
29307             dlg = this.get(dlg);
29308             if(dlg != front){
29309                 front = dlg;
29310                 dlg._lastAccess = new Date().getTime();
29311                 orderDialogs();
29312             }
29313             return dlg;
29314         },
29315
29316         /**
29317          * Sends the specified dialog to the back
29318          * @param {String/Object} dlg The id of the dialog or a dialog
29319          * @return {Roo.BasicDialog} this
29320          */
29321         sendToBack : function(dlg){
29322             dlg = this.get(dlg);
29323             dlg._lastAccess = -(new Date().getTime());
29324             orderDialogs();
29325             return dlg;
29326         },
29327
29328         /**
29329          * Hides all dialogs
29330          */
29331         hideAll : function(){
29332             for(var id in list){
29333                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29334                     list[id].hide();
29335                 }
29336             }
29337         }
29338     };
29339 }();
29340
29341 /**
29342  * @class Roo.LayoutDialog
29343  * @extends Roo.BasicDialog
29344  * Dialog which provides adjustments for working with a layout in a Dialog.
29345  * Add your necessary layout config options to the dialog's config.<br>
29346  * Example usage (including a nested layout):
29347  * <pre><code>
29348 if(!dialog){
29349     dialog = new Roo.LayoutDialog("download-dlg", {
29350         modal: true,
29351         width:600,
29352         height:450,
29353         shadow:true,
29354         minWidth:500,
29355         minHeight:350,
29356         autoTabs:true,
29357         proxyDrag:true,
29358         // layout config merges with the dialog config
29359         center:{
29360             tabPosition: "top",
29361             alwaysShowTabs: true
29362         }
29363     });
29364     dialog.addKeyListener(27, dialog.hide, dialog);
29365     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29366     dialog.addButton("Build It!", this.getDownload, this);
29367
29368     // we can even add nested layouts
29369     var innerLayout = new Roo.BorderLayout("dl-inner", {
29370         east: {
29371             initialSize: 200,
29372             autoScroll:true,
29373             split:true
29374         },
29375         center: {
29376             autoScroll:true
29377         }
29378     });
29379     innerLayout.beginUpdate();
29380     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29381     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29382     innerLayout.endUpdate(true);
29383
29384     var layout = dialog.getLayout();
29385     layout.beginUpdate();
29386     layout.add("center", new Roo.ContentPanel("standard-panel",
29387                         {title: "Download the Source", fitToFrame:true}));
29388     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29389                {title: "Build your own roo.js"}));
29390     layout.getRegion("center").showPanel(sp);
29391     layout.endUpdate();
29392 }
29393 </code></pre>
29394     * @constructor
29395     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29396     * @param {Object} config configuration options
29397   */
29398 Roo.LayoutDialog = function(el, cfg){
29399     
29400     var config=  cfg;
29401     if (typeof(cfg) == 'undefined') {
29402         config = Roo.apply({}, el);
29403         el = Roo.get( document.documentElement || document.body).createChild();
29404         //config.autoCreate = true;
29405     }
29406     
29407     
29408     config.autoTabs = false;
29409     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29410     this.body.setStyle({overflow:"hidden", position:"relative"});
29411     this.layout = new Roo.BorderLayout(this.body.dom, config);
29412     this.layout.monitorWindowResize = false;
29413     this.el.addClass("x-dlg-auto-layout");
29414     // fix case when center region overwrites center function
29415     this.center = Roo.BasicDialog.prototype.center;
29416     this.on("show", this.layout.layout, this.layout, true);
29417     if (config.items) {
29418         var xitems = config.items;
29419         delete config.items;
29420         Roo.each(xitems, this.addxtype, this);
29421     }
29422     
29423     
29424 };
29425 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29426     /**
29427      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29428      * @deprecated
29429      */
29430     endUpdate : function(){
29431         this.layout.endUpdate();
29432     },
29433
29434     /**
29435      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29436      *  @deprecated
29437      */
29438     beginUpdate : function(){
29439         this.layout.beginUpdate();
29440     },
29441
29442     /**
29443      * Get the BorderLayout for this dialog
29444      * @return {Roo.BorderLayout}
29445      */
29446     getLayout : function(){
29447         return this.layout;
29448     },
29449
29450     showEl : function(){
29451         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29452         if(Roo.isIE7){
29453             this.layout.layout();
29454         }
29455     },
29456
29457     // private
29458     // Use the syncHeightBeforeShow config option to control this automatically
29459     syncBodyHeight : function(){
29460         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29461         if(this.layout){this.layout.layout();}
29462     },
29463     
29464       /**
29465      * Add an xtype element (actually adds to the layout.)
29466      * @return {Object} xdata xtype object data.
29467      */
29468     
29469     addxtype : function(c) {
29470         return this.layout.addxtype(c);
29471     }
29472 });/*
29473  * Based on:
29474  * Ext JS Library 1.1.1
29475  * Copyright(c) 2006-2007, Ext JS, LLC.
29476  *
29477  * Originally Released Under LGPL - original licence link has changed is not relivant.
29478  *
29479  * Fork - LGPL
29480  * <script type="text/javascript">
29481  */
29482  
29483 /**
29484  * @class Roo.MessageBox
29485  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29486  * Example usage:
29487  *<pre><code>
29488 // Basic alert:
29489 Roo.Msg.alert('Status', 'Changes saved successfully.');
29490
29491 // Prompt for user data:
29492 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29493     if (btn == 'ok'){
29494         // process text value...
29495     }
29496 });
29497
29498 // Show a dialog using config options:
29499 Roo.Msg.show({
29500    title:'Save Changes?',
29501    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29502    buttons: Roo.Msg.YESNOCANCEL,
29503    fn: processResult,
29504    animEl: 'elId'
29505 });
29506 </code></pre>
29507  * @singleton
29508  */
29509 Roo.MessageBox = function(){
29510     var dlg, opt, mask, waitTimer;
29511     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29512     var buttons, activeTextEl, bwidth;
29513
29514     // private
29515     var handleButton = function(button){
29516         dlg.hide();
29517         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29518     };
29519
29520     // private
29521     var handleHide = function(){
29522         if(opt && opt.cls){
29523             dlg.el.removeClass(opt.cls);
29524         }
29525         if(waitTimer){
29526             Roo.TaskMgr.stop(waitTimer);
29527             waitTimer = null;
29528         }
29529     };
29530
29531     // private
29532     var updateButtons = function(b){
29533         var width = 0;
29534         if(!b){
29535             buttons["ok"].hide();
29536             buttons["cancel"].hide();
29537             buttons["yes"].hide();
29538             buttons["no"].hide();
29539             dlg.footer.dom.style.display = 'none';
29540             return width;
29541         }
29542         dlg.footer.dom.style.display = '';
29543         for(var k in buttons){
29544             if(typeof buttons[k] != "function"){
29545                 if(b[k]){
29546                     buttons[k].show();
29547                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29548                     width += buttons[k].el.getWidth()+15;
29549                 }else{
29550                     buttons[k].hide();
29551                 }
29552             }
29553         }
29554         return width;
29555     };
29556
29557     // private
29558     var handleEsc = function(d, k, e){
29559         if(opt && opt.closable !== false){
29560             dlg.hide();
29561         }
29562         if(e){
29563             e.stopEvent();
29564         }
29565     };
29566
29567     return {
29568         /**
29569          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29570          * @return {Roo.BasicDialog} The BasicDialog element
29571          */
29572         getDialog : function(){
29573            if(!dlg){
29574                 dlg = new Roo.BasicDialog("x-msg-box", {
29575                     autoCreate : true,
29576                     shadow: true,
29577                     draggable: true,
29578                     resizable:false,
29579                     constraintoviewport:false,
29580                     fixedcenter:true,
29581                     collapsible : false,
29582                     shim:true,
29583                     modal: true,
29584                     width:400, height:100,
29585                     buttonAlign:"center",
29586                     closeClick : function(){
29587                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29588                             handleButton("no");
29589                         }else{
29590                             handleButton("cancel");
29591                         }
29592                     }
29593                 });
29594                 dlg.on("hide", handleHide);
29595                 mask = dlg.mask;
29596                 dlg.addKeyListener(27, handleEsc);
29597                 buttons = {};
29598                 var bt = this.buttonText;
29599                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29600                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29601                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29602                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29603                 bodyEl = dlg.body.createChild({
29604
29605                     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>'
29606                 });
29607                 msgEl = bodyEl.dom.firstChild;
29608                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29609                 textboxEl.enableDisplayMode();
29610                 textboxEl.addKeyListener([10,13], function(){
29611                     if(dlg.isVisible() && opt && opt.buttons){
29612                         if(opt.buttons.ok){
29613                             handleButton("ok");
29614                         }else if(opt.buttons.yes){
29615                             handleButton("yes");
29616                         }
29617                     }
29618                 });
29619                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29620                 textareaEl.enableDisplayMode();
29621                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29622                 progressEl.enableDisplayMode();
29623                 var pf = progressEl.dom.firstChild;
29624                 if (pf) {
29625                     pp = Roo.get(pf.firstChild);
29626                     pp.setHeight(pf.offsetHeight);
29627                 }
29628                 
29629             }
29630             return dlg;
29631         },
29632
29633         /**
29634          * Updates the message box body text
29635          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29636          * the XHTML-compliant non-breaking space character '&amp;#160;')
29637          * @return {Roo.MessageBox} This message box
29638          */
29639         updateText : function(text){
29640             if(!dlg.isVisible() && !opt.width){
29641                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29642             }
29643             msgEl.innerHTML = text || '&#160;';
29644             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29645                         Math.max(opt.minWidth || this.minWidth, bwidth));
29646             if(opt.prompt){
29647                 activeTextEl.setWidth(w);
29648             }
29649             if(dlg.isVisible()){
29650                 dlg.fixedcenter = false;
29651             }
29652             dlg.setContentSize(w, bodyEl.getHeight());
29653             if(dlg.isVisible()){
29654                 dlg.fixedcenter = true;
29655             }
29656             return this;
29657         },
29658
29659         /**
29660          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29661          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29662          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29663          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29664          * @return {Roo.MessageBox} This message box
29665          */
29666         updateProgress : function(value, text){
29667             if(text){
29668                 this.updateText(text);
29669             }
29670             if (pp) { // weird bug on my firefox - for some reason this is not defined
29671                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29672             }
29673             return this;
29674         },        
29675
29676         /**
29677          * Returns true if the message box is currently displayed
29678          * @return {Boolean} True if the message box is visible, else false
29679          */
29680         isVisible : function(){
29681             return dlg && dlg.isVisible();  
29682         },
29683
29684         /**
29685          * Hides the message box if it is displayed
29686          */
29687         hide : function(){
29688             if(this.isVisible()){
29689                 dlg.hide();
29690             }  
29691         },
29692
29693         /**
29694          * Displays a new message box, or reinitializes an existing message box, based on the config options
29695          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29696          * The following config object properties are supported:
29697          * <pre>
29698 Property    Type             Description
29699 ----------  ---------------  ------------------------------------------------------------------------------------
29700 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29701                                    closes (defaults to undefined)
29702 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29703                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29704 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29705                                    progress and wait dialogs will ignore this property and always hide the
29706                                    close button as they can only be closed programmatically.
29707 cls               String           A custom CSS class to apply to the message box element
29708 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29709                                    displayed (defaults to 75)
29710 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29711                                    function will be btn (the name of the button that was clicked, if applicable,
29712                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29713                                    Progress and wait dialogs will ignore this option since they do not respond to
29714                                    user actions and can only be closed programmatically, so any required function
29715                                    should be called by the same code after it closes the dialog.
29716 icon              String           A CSS class that provides a background image to be used as an icon for
29717                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29718 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29719 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29720 modal             Boolean          False to allow user interaction with the page while the message box is
29721                                    displayed (defaults to true)
29722 msg               String           A string that will replace the existing message box body text (defaults
29723                                    to the XHTML-compliant non-breaking space character '&#160;')
29724 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29725 progress          Boolean          True to display a progress bar (defaults to false)
29726 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29727 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29728 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29729 title             String           The title text
29730 value             String           The string value to set into the active textbox element if displayed
29731 wait              Boolean          True to display a progress bar (defaults to false)
29732 width             Number           The width of the dialog in pixels
29733 </pre>
29734          *
29735          * Example usage:
29736          * <pre><code>
29737 Roo.Msg.show({
29738    title: 'Address',
29739    msg: 'Please enter your address:',
29740    width: 300,
29741    buttons: Roo.MessageBox.OKCANCEL,
29742    multiline: true,
29743    fn: saveAddress,
29744    animEl: 'addAddressBtn'
29745 });
29746 </code></pre>
29747          * @param {Object} config Configuration options
29748          * @return {Roo.MessageBox} This message box
29749          */
29750         show : function(options){
29751             if(this.isVisible()){
29752                 this.hide();
29753             }
29754             var d = this.getDialog();
29755             opt = options;
29756             d.setTitle(opt.title || "&#160;");
29757             d.close.setDisplayed(opt.closable !== false);
29758             activeTextEl = textboxEl;
29759             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29760             if(opt.prompt){
29761                 if(opt.multiline){
29762                     textboxEl.hide();
29763                     textareaEl.show();
29764                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29765                         opt.multiline : this.defaultTextHeight);
29766                     activeTextEl = textareaEl;
29767                 }else{
29768                     textboxEl.show();
29769                     textareaEl.hide();
29770                 }
29771             }else{
29772                 textboxEl.hide();
29773                 textareaEl.hide();
29774             }
29775             progressEl.setDisplayed(opt.progress === true);
29776             this.updateProgress(0);
29777             activeTextEl.dom.value = opt.value || "";
29778             if(opt.prompt){
29779                 dlg.setDefaultButton(activeTextEl);
29780             }else{
29781                 var bs = opt.buttons;
29782                 var db = null;
29783                 if(bs && bs.ok){
29784                     db = buttons["ok"];
29785                 }else if(bs && bs.yes){
29786                     db = buttons["yes"];
29787                 }
29788                 dlg.setDefaultButton(db);
29789             }
29790             bwidth = updateButtons(opt.buttons);
29791             this.updateText(opt.msg);
29792             if(opt.cls){
29793                 d.el.addClass(opt.cls);
29794             }
29795             d.proxyDrag = opt.proxyDrag === true;
29796             d.modal = opt.modal !== false;
29797             d.mask = opt.modal !== false ? mask : false;
29798             if(!d.isVisible()){
29799                 // force it to the end of the z-index stack so it gets a cursor in FF
29800                 document.body.appendChild(dlg.el.dom);
29801                 d.animateTarget = null;
29802                 d.show(options.animEl);
29803             }
29804             return this;
29805         },
29806
29807         /**
29808          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29809          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29810          * and closing the message box when the process is complete.
29811          * @param {String} title The title bar text
29812          * @param {String} msg The message box body text
29813          * @return {Roo.MessageBox} This message box
29814          */
29815         progress : function(title, msg){
29816             this.show({
29817                 title : title,
29818                 msg : msg,
29819                 buttons: false,
29820                 progress:true,
29821                 closable:false,
29822                 minWidth: this.minProgressWidth,
29823                 modal : true
29824             });
29825             return this;
29826         },
29827
29828         /**
29829          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29830          * If a callback function is passed it will be called after the user clicks the button, and the
29831          * id of the button that was clicked will be passed as the only parameter to the callback
29832          * (could also be the top-right close button).
29833          * @param {String} title The title bar text
29834          * @param {String} msg The message box body text
29835          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29836          * @param {Object} scope (optional) The scope of the callback function
29837          * @return {Roo.MessageBox} This message box
29838          */
29839         alert : function(title, msg, fn, scope){
29840             this.show({
29841                 title : title,
29842                 msg : msg,
29843                 buttons: this.OK,
29844                 fn: fn,
29845                 scope : scope,
29846                 modal : true
29847             });
29848             return this;
29849         },
29850
29851         /**
29852          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
29853          * interaction while waiting for a long-running process to complete that does not have defined intervals.
29854          * You are responsible for closing the message box when the process is complete.
29855          * @param {String} msg The message box body text
29856          * @param {String} title (optional) The title bar text
29857          * @return {Roo.MessageBox} This message box
29858          */
29859         wait : function(msg, title){
29860             this.show({
29861                 title : title,
29862                 msg : msg,
29863                 buttons: false,
29864                 closable:false,
29865                 progress:true,
29866                 modal:true,
29867                 width:300,
29868                 wait:true
29869             });
29870             waitTimer = Roo.TaskMgr.start({
29871                 run: function(i){
29872                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
29873                 },
29874                 interval: 1000
29875             });
29876             return this;
29877         },
29878
29879         /**
29880          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
29881          * If a callback function is passed it will be called after the user clicks either button, and the id of the
29882          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
29883          * @param {String} title The title bar text
29884          * @param {String} msg The message box body text
29885          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29886          * @param {Object} scope (optional) The scope of the callback function
29887          * @return {Roo.MessageBox} This message box
29888          */
29889         confirm : function(title, msg, fn, scope){
29890             this.show({
29891                 title : title,
29892                 msg : msg,
29893                 buttons: this.YESNO,
29894                 fn: fn,
29895                 scope : scope,
29896                 modal : true
29897             });
29898             return this;
29899         },
29900
29901         /**
29902          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
29903          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
29904          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
29905          * (could also be the top-right close button) and the text that was entered will be passed as the two
29906          * parameters to the callback.
29907          * @param {String} title The title bar text
29908          * @param {String} msg The message box body text
29909          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29910          * @param {Object} scope (optional) The scope of the callback function
29911          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
29912          * property, or the height in pixels to create the textbox (defaults to false / single-line)
29913          * @return {Roo.MessageBox} This message box
29914          */
29915         prompt : function(title, msg, fn, scope, multiline){
29916             this.show({
29917                 title : title,
29918                 msg : msg,
29919                 buttons: this.OKCANCEL,
29920                 fn: fn,
29921                 minWidth:250,
29922                 scope : scope,
29923                 prompt:true,
29924                 multiline: multiline,
29925                 modal : true
29926             });
29927             return this;
29928         },
29929
29930         /**
29931          * Button config that displays a single OK button
29932          * @type Object
29933          */
29934         OK : {ok:true},
29935         /**
29936          * Button config that displays Yes and No buttons
29937          * @type Object
29938          */
29939         YESNO : {yes:true, no:true},
29940         /**
29941          * Button config that displays OK and Cancel buttons
29942          * @type Object
29943          */
29944         OKCANCEL : {ok:true, cancel:true},
29945         /**
29946          * Button config that displays Yes, No and Cancel buttons
29947          * @type Object
29948          */
29949         YESNOCANCEL : {yes:true, no:true, cancel:true},
29950
29951         /**
29952          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
29953          * @type Number
29954          */
29955         defaultTextHeight : 75,
29956         /**
29957          * The maximum width in pixels of the message box (defaults to 600)
29958          * @type Number
29959          */
29960         maxWidth : 600,
29961         /**
29962          * The minimum width in pixels of the message box (defaults to 100)
29963          * @type Number
29964          */
29965         minWidth : 100,
29966         /**
29967          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
29968          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
29969          * @type Number
29970          */
29971         minProgressWidth : 250,
29972         /**
29973          * An object containing the default button text strings that can be overriden for localized language support.
29974          * Supported properties are: ok, cancel, yes and no.
29975          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
29976          * @type Object
29977          */
29978         buttonText : {
29979             ok : "OK",
29980             cancel : "Cancel",
29981             yes : "Yes",
29982             no : "No"
29983         }
29984     };
29985 }();
29986
29987 /**
29988  * Shorthand for {@link Roo.MessageBox}
29989  */
29990 Roo.Msg = Roo.MessageBox;/*
29991  * Based on:
29992  * Ext JS Library 1.1.1
29993  * Copyright(c) 2006-2007, Ext JS, LLC.
29994  *
29995  * Originally Released Under LGPL - original licence link has changed is not relivant.
29996  *
29997  * Fork - LGPL
29998  * <script type="text/javascript">
29999  */
30000 /**
30001  * @class Roo.QuickTips
30002  * Provides attractive and customizable tooltips for any element.
30003  * @singleton
30004  */
30005 Roo.QuickTips = function(){
30006     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30007     var ce, bd, xy, dd;
30008     var visible = false, disabled = true, inited = false;
30009     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30010     
30011     var onOver = function(e){
30012         if(disabled){
30013             return;
30014         }
30015         var t = e.getTarget();
30016         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30017             return;
30018         }
30019         if(ce && t == ce.el){
30020             clearTimeout(hideProc);
30021             return;
30022         }
30023         if(t && tagEls[t.id]){
30024             tagEls[t.id].el = t;
30025             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30026             return;
30027         }
30028         var ttp, et = Roo.fly(t);
30029         var ns = cfg.namespace;
30030         if(tm.interceptTitles && t.title){
30031             ttp = t.title;
30032             t.qtip = ttp;
30033             t.removeAttribute("title");
30034             e.preventDefault();
30035         }else{
30036             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30037         }
30038         if(ttp){
30039             showProc = show.defer(tm.showDelay, tm, [{
30040                 el: t, 
30041                 text: ttp, 
30042                 width: et.getAttributeNS(ns, cfg.width),
30043                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30044                 title: et.getAttributeNS(ns, cfg.title),
30045                     cls: et.getAttributeNS(ns, cfg.cls)
30046             }]);
30047         }
30048     };
30049     
30050     var onOut = function(e){
30051         clearTimeout(showProc);
30052         var t = e.getTarget();
30053         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30054             hideProc = setTimeout(hide, tm.hideDelay);
30055         }
30056     };
30057     
30058     var onMove = function(e){
30059         if(disabled){
30060             return;
30061         }
30062         xy = e.getXY();
30063         xy[1] += 18;
30064         if(tm.trackMouse && ce){
30065             el.setXY(xy);
30066         }
30067     };
30068     
30069     var onDown = function(e){
30070         clearTimeout(showProc);
30071         clearTimeout(hideProc);
30072         if(!e.within(el)){
30073             if(tm.hideOnClick){
30074                 hide();
30075                 tm.disable();
30076                 tm.enable.defer(100, tm);
30077             }
30078         }
30079     };
30080     
30081     var getPad = function(){
30082         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30083     };
30084
30085     var show = function(o){
30086         if(disabled){
30087             return;
30088         }
30089         clearTimeout(dismissProc);
30090         ce = o;
30091         if(removeCls){ // in case manually hidden
30092             el.removeClass(removeCls);
30093             removeCls = null;
30094         }
30095         if(ce.cls){
30096             el.addClass(ce.cls);
30097             removeCls = ce.cls;
30098         }
30099         if(ce.title){
30100             tipTitle.update(ce.title);
30101             tipTitle.show();
30102         }else{
30103             tipTitle.update('');
30104             tipTitle.hide();
30105         }
30106         el.dom.style.width  = tm.maxWidth+'px';
30107         //tipBody.dom.style.width = '';
30108         tipBodyText.update(o.text);
30109         var p = getPad(), w = ce.width;
30110         if(!w){
30111             var td = tipBodyText.dom;
30112             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30113             if(aw > tm.maxWidth){
30114                 w = tm.maxWidth;
30115             }else if(aw < tm.minWidth){
30116                 w = tm.minWidth;
30117             }else{
30118                 w = aw;
30119             }
30120         }
30121         //tipBody.setWidth(w);
30122         el.setWidth(parseInt(w, 10) + p);
30123         if(ce.autoHide === false){
30124             close.setDisplayed(true);
30125             if(dd){
30126                 dd.unlock();
30127             }
30128         }else{
30129             close.setDisplayed(false);
30130             if(dd){
30131                 dd.lock();
30132             }
30133         }
30134         if(xy){
30135             el.avoidY = xy[1]-18;
30136             el.setXY(xy);
30137         }
30138         if(tm.animate){
30139             el.setOpacity(.1);
30140             el.setStyle("visibility", "visible");
30141             el.fadeIn({callback: afterShow});
30142         }else{
30143             afterShow();
30144         }
30145     };
30146     
30147     var afterShow = function(){
30148         if(ce){
30149             el.show();
30150             esc.enable();
30151             if(tm.autoDismiss && ce.autoHide !== false){
30152                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30153             }
30154         }
30155     };
30156     
30157     var hide = function(noanim){
30158         clearTimeout(dismissProc);
30159         clearTimeout(hideProc);
30160         ce = null;
30161         if(el.isVisible()){
30162             esc.disable();
30163             if(noanim !== true && tm.animate){
30164                 el.fadeOut({callback: afterHide});
30165             }else{
30166                 afterHide();
30167             } 
30168         }
30169     };
30170     
30171     var afterHide = function(){
30172         el.hide();
30173         if(removeCls){
30174             el.removeClass(removeCls);
30175             removeCls = null;
30176         }
30177     };
30178     
30179     return {
30180         /**
30181         * @cfg {Number} minWidth
30182         * The minimum width of the quick tip (defaults to 40)
30183         */
30184        minWidth : 40,
30185         /**
30186         * @cfg {Number} maxWidth
30187         * The maximum width of the quick tip (defaults to 300)
30188         */
30189        maxWidth : 300,
30190         /**
30191         * @cfg {Boolean} interceptTitles
30192         * True to automatically use the element's DOM title value if available (defaults to false)
30193         */
30194        interceptTitles : false,
30195         /**
30196         * @cfg {Boolean} trackMouse
30197         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30198         */
30199        trackMouse : false,
30200         /**
30201         * @cfg {Boolean} hideOnClick
30202         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30203         */
30204        hideOnClick : true,
30205         /**
30206         * @cfg {Number} showDelay
30207         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30208         */
30209        showDelay : 500,
30210         /**
30211         * @cfg {Number} hideDelay
30212         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30213         */
30214        hideDelay : 200,
30215         /**
30216         * @cfg {Boolean} autoHide
30217         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30218         * Used in conjunction with hideDelay.
30219         */
30220        autoHide : true,
30221         /**
30222         * @cfg {Boolean}
30223         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30224         * (defaults to true).  Used in conjunction with autoDismissDelay.
30225         */
30226        autoDismiss : true,
30227         /**
30228         * @cfg {Number}
30229         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30230         */
30231        autoDismissDelay : 5000,
30232        /**
30233         * @cfg {Boolean} animate
30234         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30235         */
30236        animate : false,
30237
30238        /**
30239         * @cfg {String} title
30240         * Title text to display (defaults to '').  This can be any valid HTML markup.
30241         */
30242         title: '',
30243        /**
30244         * @cfg {String} text
30245         * Body text to display (defaults to '').  This can be any valid HTML markup.
30246         */
30247         text : '',
30248        /**
30249         * @cfg {String} cls
30250         * A CSS class to apply to the base quick tip element (defaults to '').
30251         */
30252         cls : '',
30253        /**
30254         * @cfg {Number} width
30255         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30256         * minWidth or maxWidth.
30257         */
30258         width : null,
30259
30260     /**
30261      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30262      * or display QuickTips in a page.
30263      */
30264        init : function(){
30265           tm = Roo.QuickTips;
30266           cfg = tm.tagConfig;
30267           if(!inited){
30268               if(!Roo.isReady){ // allow calling of init() before onReady
30269                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30270                   return;
30271               }
30272               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30273               el.fxDefaults = {stopFx: true};
30274               // maximum custom styling
30275               //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>');
30276               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>');              
30277               tipTitle = el.child('h3');
30278               tipTitle.enableDisplayMode("block");
30279               tipBody = el.child('div.x-tip-bd');
30280               tipBodyText = el.child('div.x-tip-bd-inner');
30281               //bdLeft = el.child('div.x-tip-bd-left');
30282               //bdRight = el.child('div.x-tip-bd-right');
30283               close = el.child('div.x-tip-close');
30284               close.enableDisplayMode("block");
30285               close.on("click", hide);
30286               var d = Roo.get(document);
30287               d.on("mousedown", onDown);
30288               d.on("mouseover", onOver);
30289               d.on("mouseout", onOut);
30290               d.on("mousemove", onMove);
30291               esc = d.addKeyListener(27, hide);
30292               esc.disable();
30293               if(Roo.dd.DD){
30294                   dd = el.initDD("default", null, {
30295                       onDrag : function(){
30296                           el.sync();  
30297                       }
30298                   });
30299                   dd.setHandleElId(tipTitle.id);
30300                   dd.lock();
30301               }
30302               inited = true;
30303           }
30304           this.enable(); 
30305        },
30306
30307     /**
30308      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30309      * are supported:
30310      * <pre>
30311 Property    Type                   Description
30312 ----------  ---------------------  ------------------------------------------------------------------------
30313 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30314      * </ul>
30315      * @param {Object} config The config object
30316      */
30317        register : function(config){
30318            var cs = config instanceof Array ? config : arguments;
30319            for(var i = 0, len = cs.length; i < len; i++) {
30320                var c = cs[i];
30321                var target = c.target;
30322                if(target){
30323                    if(target instanceof Array){
30324                        for(var j = 0, jlen = target.length; j < jlen; j++){
30325                            tagEls[target[j]] = c;
30326                        }
30327                    }else{
30328                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30329                    }
30330                }
30331            }
30332        },
30333
30334     /**
30335      * Removes this quick tip from its element and destroys it.
30336      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30337      */
30338        unregister : function(el){
30339            delete tagEls[Roo.id(el)];
30340        },
30341
30342     /**
30343      * Enable this quick tip.
30344      */
30345        enable : function(){
30346            if(inited && disabled){
30347                locks.pop();
30348                if(locks.length < 1){
30349                    disabled = false;
30350                }
30351            }
30352        },
30353
30354     /**
30355      * Disable this quick tip.
30356      */
30357        disable : function(){
30358           disabled = true;
30359           clearTimeout(showProc);
30360           clearTimeout(hideProc);
30361           clearTimeout(dismissProc);
30362           if(ce){
30363               hide(true);
30364           }
30365           locks.push(1);
30366        },
30367
30368     /**
30369      * Returns true if the quick tip is enabled, else false.
30370      */
30371        isEnabled : function(){
30372             return !disabled;
30373        },
30374
30375         // private
30376        tagConfig : {
30377            namespace : "ext",
30378            attribute : "qtip",
30379            width : "width",
30380            target : "target",
30381            title : "qtitle",
30382            hide : "hide",
30383            cls : "qclass"
30384        }
30385    };
30386 }();
30387
30388 // backwards compat
30389 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30390  * Based on:
30391  * Ext JS Library 1.1.1
30392  * Copyright(c) 2006-2007, Ext JS, LLC.
30393  *
30394  * Originally Released Under LGPL - original licence link has changed is not relivant.
30395  *
30396  * Fork - LGPL
30397  * <script type="text/javascript">
30398  */
30399  
30400
30401 /**
30402  * @class Roo.tree.TreePanel
30403  * @extends Roo.data.Tree
30404
30405  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30406  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30407  * @cfg {Boolean} enableDD true to enable drag and drop
30408  * @cfg {Boolean} enableDrag true to enable just drag
30409  * @cfg {Boolean} enableDrop true to enable just drop
30410  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30411  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30412  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30413  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30414  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30415  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30416  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30417  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30418  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30419  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30420  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30421  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30422  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30423  * @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>
30424  * @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>
30425  * 
30426  * @constructor
30427  * @param {String/HTMLElement/Element} el The container element
30428  * @param {Object} config
30429  */
30430 Roo.tree.TreePanel = function(el, config){
30431     var root = false;
30432     var loader = false;
30433     if (config.root) {
30434         root = config.root;
30435         delete config.root;
30436     }
30437     if (config.loader) {
30438         loader = config.loader;
30439         delete config.loader;
30440     }
30441     
30442     Roo.apply(this, config);
30443     Roo.tree.TreePanel.superclass.constructor.call(this);
30444     this.el = Roo.get(el);
30445     this.el.addClass('x-tree');
30446     //console.log(root);
30447     if (root) {
30448         this.setRootNode( Roo.factory(root, Roo.tree));
30449     }
30450     if (loader) {
30451         this.loader = Roo.factory(loader, Roo.tree);
30452     }
30453    /**
30454     * Read-only. The id of the container element becomes this TreePanel's id.
30455     */
30456    this.id = this.el.id;
30457    this.addEvents({
30458         /**
30459         * @event beforeload
30460         * Fires before a node is loaded, return false to cancel
30461         * @param {Node} node The node being loaded
30462         */
30463         "beforeload" : true,
30464         /**
30465         * @event load
30466         * Fires when a node is loaded
30467         * @param {Node} node The node that was loaded
30468         */
30469         "load" : true,
30470         /**
30471         * @event textchange
30472         * Fires when the text for a node is changed
30473         * @param {Node} node The node
30474         * @param {String} text The new text
30475         * @param {String} oldText The old text
30476         */
30477         "textchange" : true,
30478         /**
30479         * @event beforeexpand
30480         * Fires before a node is expanded, return false to cancel.
30481         * @param {Node} node The node
30482         * @param {Boolean} deep
30483         * @param {Boolean} anim
30484         */
30485         "beforeexpand" : true,
30486         /**
30487         * @event beforecollapse
30488         * Fires before a node is collapsed, return false to cancel.
30489         * @param {Node} node The node
30490         * @param {Boolean} deep
30491         * @param {Boolean} anim
30492         */
30493         "beforecollapse" : true,
30494         /**
30495         * @event expand
30496         * Fires when a node is expanded
30497         * @param {Node} node The node
30498         */
30499         "expand" : true,
30500         /**
30501         * @event disabledchange
30502         * Fires when the disabled status of a node changes
30503         * @param {Node} node The node
30504         * @param {Boolean} disabled
30505         */
30506         "disabledchange" : true,
30507         /**
30508         * @event collapse
30509         * Fires when a node is collapsed
30510         * @param {Node} node The node
30511         */
30512         "collapse" : true,
30513         /**
30514         * @event beforeclick
30515         * Fires before click processing on a node. Return false to cancel the default action.
30516         * @param {Node} node The node
30517         * @param {Roo.EventObject} e The event object
30518         */
30519         "beforeclick":true,
30520         /**
30521         * @event checkchange
30522         * Fires when a node with a checkbox's checked property changes
30523         * @param {Node} this This node
30524         * @param {Boolean} checked
30525         */
30526         "checkchange":true,
30527         /**
30528         * @event click
30529         * Fires when a node is clicked
30530         * @param {Node} node The node
30531         * @param {Roo.EventObject} e The event object
30532         */
30533         "click":true,
30534         /**
30535         * @event dblclick
30536         * Fires when a node is double clicked
30537         * @param {Node} node The node
30538         * @param {Roo.EventObject} e The event object
30539         */
30540         "dblclick":true,
30541         /**
30542         * @event contextmenu
30543         * Fires when a node is right clicked
30544         * @param {Node} node The node
30545         * @param {Roo.EventObject} e The event object
30546         */
30547         "contextmenu":true,
30548         /**
30549         * @event beforechildrenrendered
30550         * Fires right before the child nodes for a node are rendered
30551         * @param {Node} node The node
30552         */
30553         "beforechildrenrendered":true,
30554        /**
30555              * @event startdrag
30556              * Fires when a node starts being dragged
30557              * @param {Roo.tree.TreePanel} this
30558              * @param {Roo.tree.TreeNode} node
30559              * @param {event} e The raw browser event
30560              */ 
30561             "startdrag" : true,
30562             /**
30563              * @event enddrag
30564              * Fires when a drag operation is complete
30565              * @param {Roo.tree.TreePanel} this
30566              * @param {Roo.tree.TreeNode} node
30567              * @param {event} e The raw browser event
30568              */
30569             "enddrag" : true,
30570             /**
30571              * @event dragdrop
30572              * Fires when a dragged node is dropped on a valid DD target
30573              * @param {Roo.tree.TreePanel} this
30574              * @param {Roo.tree.TreeNode} node
30575              * @param {DD} dd The dd it was dropped on
30576              * @param {event} e The raw browser event
30577              */
30578             "dragdrop" : true,
30579             /**
30580              * @event beforenodedrop
30581              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30582              * passed to handlers has the following properties:<br />
30583              * <ul style="padding:5px;padding-left:16px;">
30584              * <li>tree - The TreePanel</li>
30585              * <li>target - The node being targeted for the drop</li>
30586              * <li>data - The drag data from the drag source</li>
30587              * <li>point - The point of the drop - append, above or below</li>
30588              * <li>source - The drag source</li>
30589              * <li>rawEvent - Raw mouse event</li>
30590              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30591              * to be inserted by setting them on this object.</li>
30592              * <li>cancel - Set this to true to cancel the drop.</li>
30593              * </ul>
30594              * @param {Object} dropEvent
30595              */
30596             "beforenodedrop" : true,
30597             /**
30598              * @event nodedrop
30599              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30600              * passed to handlers has the following properties:<br />
30601              * <ul style="padding:5px;padding-left:16px;">
30602              * <li>tree - The TreePanel</li>
30603              * <li>target - The node being targeted for the drop</li>
30604              * <li>data - The drag data from the drag source</li>
30605              * <li>point - The point of the drop - append, above or below</li>
30606              * <li>source - The drag source</li>
30607              * <li>rawEvent - Raw mouse event</li>
30608              * <li>dropNode - Dropped node(s).</li>
30609              * </ul>
30610              * @param {Object} dropEvent
30611              */
30612             "nodedrop" : true,
30613              /**
30614              * @event nodedragover
30615              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30616              * passed to handlers has the following properties:<br />
30617              * <ul style="padding:5px;padding-left:16px;">
30618              * <li>tree - The TreePanel</li>
30619              * <li>target - The node being targeted for the drop</li>
30620              * <li>data - The drag data from the drag source</li>
30621              * <li>point - The point of the drop - append, above or below</li>
30622              * <li>source - The drag source</li>
30623              * <li>rawEvent - Raw mouse event</li>
30624              * <li>dropNode - Drop node(s) provided by the source.</li>
30625              * <li>cancel - Set this to true to signal drop not allowed.</li>
30626              * </ul>
30627              * @param {Object} dragOverEvent
30628              */
30629             "nodedragover" : true
30630         
30631    });
30632    if(this.singleExpand){
30633        this.on("beforeexpand", this.restrictExpand, this);
30634    }
30635 };
30636 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30637     rootVisible : true,
30638     animate: Roo.enableFx,
30639     lines : true,
30640     enableDD : false,
30641     hlDrop : Roo.enableFx,
30642   
30643     renderer: false,
30644     
30645     rendererTip: false,
30646     // private
30647     restrictExpand : function(node){
30648         var p = node.parentNode;
30649         if(p){
30650             if(p.expandedChild && p.expandedChild.parentNode == p){
30651                 p.expandedChild.collapse();
30652             }
30653             p.expandedChild = node;
30654         }
30655     },
30656
30657     // private override
30658     setRootNode : function(node){
30659         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30660         if(!this.rootVisible){
30661             node.ui = new Roo.tree.RootTreeNodeUI(node);
30662         }
30663         return node;
30664     },
30665
30666     /**
30667      * Returns the container element for this TreePanel
30668      */
30669     getEl : function(){
30670         return this.el;
30671     },
30672
30673     /**
30674      * Returns the default TreeLoader for this TreePanel
30675      */
30676     getLoader : function(){
30677         return this.loader;
30678     },
30679
30680     /**
30681      * Expand all nodes
30682      */
30683     expandAll : function(){
30684         this.root.expand(true);
30685     },
30686
30687     /**
30688      * Collapse all nodes
30689      */
30690     collapseAll : function(){
30691         this.root.collapse(true);
30692     },
30693
30694     /**
30695      * Returns the selection model used by this TreePanel
30696      */
30697     getSelectionModel : function(){
30698         if(!this.selModel){
30699             this.selModel = new Roo.tree.DefaultSelectionModel();
30700         }
30701         return this.selModel;
30702     },
30703
30704     /**
30705      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30706      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30707      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30708      * @return {Array}
30709      */
30710     getChecked : function(a, startNode){
30711         startNode = startNode || this.root;
30712         var r = [];
30713         var f = function(){
30714             if(this.attributes.checked){
30715                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30716             }
30717         }
30718         startNode.cascade(f);
30719         return r;
30720     },
30721
30722     /**
30723      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30724      * @param {String} path
30725      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30726      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30727      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30728      */
30729     expandPath : function(path, attr, callback){
30730         attr = attr || "id";
30731         var keys = path.split(this.pathSeparator);
30732         var curNode = this.root;
30733         if(curNode.attributes[attr] != keys[1]){ // invalid root
30734             if(callback){
30735                 callback(false, null);
30736             }
30737             return;
30738         }
30739         var index = 1;
30740         var f = function(){
30741             if(++index == keys.length){
30742                 if(callback){
30743                     callback(true, curNode);
30744                 }
30745                 return;
30746             }
30747             var c = curNode.findChild(attr, keys[index]);
30748             if(!c){
30749                 if(callback){
30750                     callback(false, curNode);
30751                 }
30752                 return;
30753             }
30754             curNode = c;
30755             c.expand(false, false, f);
30756         };
30757         curNode.expand(false, false, f);
30758     },
30759
30760     /**
30761      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30762      * @param {String} path
30763      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30764      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30765      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30766      */
30767     selectPath : function(path, attr, callback){
30768         attr = attr || "id";
30769         var keys = path.split(this.pathSeparator);
30770         var v = keys.pop();
30771         if(keys.length > 0){
30772             var f = function(success, node){
30773                 if(success && node){
30774                     var n = node.findChild(attr, v);
30775                     if(n){
30776                         n.select();
30777                         if(callback){
30778                             callback(true, n);
30779                         }
30780                     }else if(callback){
30781                         callback(false, n);
30782                     }
30783                 }else{
30784                     if(callback){
30785                         callback(false, n);
30786                     }
30787                 }
30788             };
30789             this.expandPath(keys.join(this.pathSeparator), attr, f);
30790         }else{
30791             this.root.select();
30792             if(callback){
30793                 callback(true, this.root);
30794             }
30795         }
30796     },
30797
30798     getTreeEl : function(){
30799         return this.el;
30800     },
30801
30802     /**
30803      * Trigger rendering of this TreePanel
30804      */
30805     render : function(){
30806         if (this.innerCt) {
30807             return this; // stop it rendering more than once!!
30808         }
30809         
30810         this.innerCt = this.el.createChild({tag:"ul",
30811                cls:"x-tree-root-ct " +
30812                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30813
30814         if(this.containerScroll){
30815             Roo.dd.ScrollManager.register(this.el);
30816         }
30817         if((this.enableDD || this.enableDrop) && !this.dropZone){
30818            /**
30819             * The dropZone used by this tree if drop is enabled
30820             * @type Roo.tree.TreeDropZone
30821             */
30822              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30823                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30824            });
30825         }
30826         if((this.enableDD || this.enableDrag) && !this.dragZone){
30827            /**
30828             * The dragZone used by this tree if drag is enabled
30829             * @type Roo.tree.TreeDragZone
30830             */
30831             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30832                ddGroup: this.ddGroup || "TreeDD",
30833                scroll: this.ddScroll
30834            });
30835         }
30836         this.getSelectionModel().init(this);
30837         if (!this.root) {
30838             console.log("ROOT not set in tree");
30839             return;
30840         }
30841         this.root.render();
30842         if(!this.rootVisible){
30843             this.root.renderChildren();
30844         }
30845         return this;
30846     }
30847 });/*
30848  * Based on:
30849  * Ext JS Library 1.1.1
30850  * Copyright(c) 2006-2007, Ext JS, LLC.
30851  *
30852  * Originally Released Under LGPL - original licence link has changed is not relivant.
30853  *
30854  * Fork - LGPL
30855  * <script type="text/javascript">
30856  */
30857  
30858
30859 /**
30860  * @class Roo.tree.DefaultSelectionModel
30861  * @extends Roo.util.Observable
30862  * The default single selection for a TreePanel.
30863  */
30864 Roo.tree.DefaultSelectionModel = function(){
30865    this.selNode = null;
30866    
30867    this.addEvents({
30868        /**
30869         * @event selectionchange
30870         * Fires when the selected node changes
30871         * @param {DefaultSelectionModel} this
30872         * @param {TreeNode} node the new selection
30873         */
30874        "selectionchange" : true,
30875
30876        /**
30877         * @event beforeselect
30878         * Fires before the selected node changes, return false to cancel the change
30879         * @param {DefaultSelectionModel} this
30880         * @param {TreeNode} node the new selection
30881         * @param {TreeNode} node the old selection
30882         */
30883        "beforeselect" : true
30884    });
30885 };
30886
30887 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
30888     init : function(tree){
30889         this.tree = tree;
30890         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30891         tree.on("click", this.onNodeClick, this);
30892     },
30893     
30894     onNodeClick : function(node, e){
30895         if (e.ctrlKey && this.selNode == node)  {
30896             this.unselect(node);
30897             return;
30898         }
30899         this.select(node);
30900     },
30901     
30902     /**
30903      * Select a node.
30904      * @param {TreeNode} node The node to select
30905      * @return {TreeNode} The selected node
30906      */
30907     select : function(node){
30908         var last = this.selNode;
30909         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
30910             if(last){
30911                 last.ui.onSelectedChange(false);
30912             }
30913             this.selNode = node;
30914             node.ui.onSelectedChange(true);
30915             this.fireEvent("selectionchange", this, node, last);
30916         }
30917         return node;
30918     },
30919     
30920     /**
30921      * Deselect a node.
30922      * @param {TreeNode} node The node to unselect
30923      */
30924     unselect : function(node){
30925         if(this.selNode == node){
30926             this.clearSelections();
30927         }    
30928     },
30929     
30930     /**
30931      * Clear all selections
30932      */
30933     clearSelections : function(){
30934         var n = this.selNode;
30935         if(n){
30936             n.ui.onSelectedChange(false);
30937             this.selNode = null;
30938             this.fireEvent("selectionchange", this, null);
30939         }
30940         return n;
30941     },
30942     
30943     /**
30944      * Get the selected node
30945      * @return {TreeNode} The selected node
30946      */
30947     getSelectedNode : function(){
30948         return this.selNode;    
30949     },
30950     
30951     /**
30952      * Returns true if the node is selected
30953      * @param {TreeNode} node The node to check
30954      * @return {Boolean}
30955      */
30956     isSelected : function(node){
30957         return this.selNode == node;  
30958     },
30959
30960     /**
30961      * Selects the node above the selected node in the tree, intelligently walking the nodes
30962      * @return TreeNode The new selection
30963      */
30964     selectPrevious : function(){
30965         var s = this.selNode || this.lastSelNode;
30966         if(!s){
30967             return null;
30968         }
30969         var ps = s.previousSibling;
30970         if(ps){
30971             if(!ps.isExpanded() || ps.childNodes.length < 1){
30972                 return this.select(ps);
30973             } else{
30974                 var lc = ps.lastChild;
30975                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
30976                     lc = lc.lastChild;
30977                 }
30978                 return this.select(lc);
30979             }
30980         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
30981             return this.select(s.parentNode);
30982         }
30983         return null;
30984     },
30985
30986     /**
30987      * Selects the node above the selected node in the tree, intelligently walking the nodes
30988      * @return TreeNode The new selection
30989      */
30990     selectNext : function(){
30991         var s = this.selNode || this.lastSelNode;
30992         if(!s){
30993             return null;
30994         }
30995         if(s.firstChild && s.isExpanded()){
30996              return this.select(s.firstChild);
30997          }else if(s.nextSibling){
30998              return this.select(s.nextSibling);
30999          }else if(s.parentNode){
31000             var newS = null;
31001             s.parentNode.bubble(function(){
31002                 if(this.nextSibling){
31003                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31004                     return false;
31005                 }
31006             });
31007             return newS;
31008          }
31009         return null;
31010     },
31011
31012     onKeyDown : function(e){
31013         var s = this.selNode || this.lastSelNode;
31014         // undesirable, but required
31015         var sm = this;
31016         if(!s){
31017             return;
31018         }
31019         var k = e.getKey();
31020         switch(k){
31021              case e.DOWN:
31022                  e.stopEvent();
31023                  this.selectNext();
31024              break;
31025              case e.UP:
31026                  e.stopEvent();
31027                  this.selectPrevious();
31028              break;
31029              case e.RIGHT:
31030                  e.preventDefault();
31031                  if(s.hasChildNodes()){
31032                      if(!s.isExpanded()){
31033                          s.expand();
31034                      }else if(s.firstChild){
31035                          this.select(s.firstChild, e);
31036                      }
31037                  }
31038              break;
31039              case e.LEFT:
31040                  e.preventDefault();
31041                  if(s.hasChildNodes() && s.isExpanded()){
31042                      s.collapse();
31043                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31044                      this.select(s.parentNode, e);
31045                  }
31046              break;
31047         };
31048     }
31049 });
31050
31051 /**
31052  * @class Roo.tree.MultiSelectionModel
31053  * @extends Roo.util.Observable
31054  * Multi selection for a TreePanel.
31055  */
31056 Roo.tree.MultiSelectionModel = function(){
31057    this.selNodes = [];
31058    this.selMap = {};
31059    this.addEvents({
31060        /**
31061         * @event selectionchange
31062         * Fires when the selected nodes change
31063         * @param {MultiSelectionModel} this
31064         * @param {Array} nodes Array of the selected nodes
31065         */
31066        "selectionchange" : true
31067    });
31068 };
31069
31070 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31071     init : function(tree){
31072         this.tree = tree;
31073         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31074         tree.on("click", this.onNodeClick, this);
31075     },
31076     
31077     onNodeClick : function(node, e){
31078         this.select(node, e, e.ctrlKey);
31079     },
31080     
31081     /**
31082      * Select a node.
31083      * @param {TreeNode} node The node to select
31084      * @param {EventObject} e (optional) An event associated with the selection
31085      * @param {Boolean} keepExisting True to retain existing selections
31086      * @return {TreeNode} The selected node
31087      */
31088     select : function(node, e, keepExisting){
31089         if(keepExisting !== true){
31090             this.clearSelections(true);
31091         }
31092         if(this.isSelected(node)){
31093             this.lastSelNode = node;
31094             return node;
31095         }
31096         this.selNodes.push(node);
31097         this.selMap[node.id] = node;
31098         this.lastSelNode = node;
31099         node.ui.onSelectedChange(true);
31100         this.fireEvent("selectionchange", this, this.selNodes);
31101         return node;
31102     },
31103     
31104     /**
31105      * Deselect a node.
31106      * @param {TreeNode} node The node to unselect
31107      */
31108     unselect : function(node){
31109         if(this.selMap[node.id]){
31110             node.ui.onSelectedChange(false);
31111             var sn = this.selNodes;
31112             var index = -1;
31113             if(sn.indexOf){
31114                 index = sn.indexOf(node);
31115             }else{
31116                 for(var i = 0, len = sn.length; i < len; i++){
31117                     if(sn[i] == node){
31118                         index = i;
31119                         break;
31120                     }
31121                 }
31122             }
31123             if(index != -1){
31124                 this.selNodes.splice(index, 1);
31125             }
31126             delete this.selMap[node.id];
31127             this.fireEvent("selectionchange", this, this.selNodes);
31128         }
31129     },
31130     
31131     /**
31132      * Clear all selections
31133      */
31134     clearSelections : function(suppressEvent){
31135         var sn = this.selNodes;
31136         if(sn.length > 0){
31137             for(var i = 0, len = sn.length; i < len; i++){
31138                 sn[i].ui.onSelectedChange(false);
31139             }
31140             this.selNodes = [];
31141             this.selMap = {};
31142             if(suppressEvent !== true){
31143                 this.fireEvent("selectionchange", this, this.selNodes);
31144             }
31145         }
31146     },
31147     
31148     /**
31149      * Returns true if the node is selected
31150      * @param {TreeNode} node The node to check
31151      * @return {Boolean}
31152      */
31153     isSelected : function(node){
31154         return this.selMap[node.id] ? true : false;  
31155     },
31156     
31157     /**
31158      * Returns an array of the selected nodes
31159      * @return {Array}
31160      */
31161     getSelectedNodes : function(){
31162         return this.selNodes;    
31163     },
31164
31165     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31166
31167     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31168
31169     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31170 });/*
31171  * Based on:
31172  * Ext JS Library 1.1.1
31173  * Copyright(c) 2006-2007, Ext JS, LLC.
31174  *
31175  * Originally Released Under LGPL - original licence link has changed is not relivant.
31176  *
31177  * Fork - LGPL
31178  * <script type="text/javascript">
31179  */
31180  
31181 /**
31182  * @class Roo.tree.TreeNode
31183  * @extends Roo.data.Node
31184  * @cfg {String} text The text for this node
31185  * @cfg {Boolean} expanded true to start the node expanded
31186  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31187  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31188  * @cfg {Boolean} disabled true to start the node disabled
31189  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31190  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31191  * @cfg {String} cls A css class to be added to the node
31192  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31193  * @cfg {String} href URL of the link used for the node (defaults to #)
31194  * @cfg {String} hrefTarget target frame for the link
31195  * @cfg {String} qtip An Ext QuickTip for the node
31196  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31197  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31198  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31199  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31200  * (defaults to undefined with no checkbox rendered)
31201  * @constructor
31202  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31203  */
31204 Roo.tree.TreeNode = function(attributes){
31205     attributes = attributes || {};
31206     if(typeof attributes == "string"){
31207         attributes = {text: attributes};
31208     }
31209     this.childrenRendered = false;
31210     this.rendered = false;
31211     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31212     this.expanded = attributes.expanded === true;
31213     this.isTarget = attributes.isTarget !== false;
31214     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31215     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31216
31217     /**
31218      * Read-only. The text for this node. To change it use setText().
31219      * @type String
31220      */
31221     this.text = attributes.text;
31222     /**
31223      * True if this node is disabled.
31224      * @type Boolean
31225      */
31226     this.disabled = attributes.disabled === true;
31227
31228     this.addEvents({
31229         /**
31230         * @event textchange
31231         * Fires when the text for this node is changed
31232         * @param {Node} this This node
31233         * @param {String} text The new text
31234         * @param {String} oldText The old text
31235         */
31236         "textchange" : true,
31237         /**
31238         * @event beforeexpand
31239         * Fires before this node is expanded, return false to cancel.
31240         * @param {Node} this This node
31241         * @param {Boolean} deep
31242         * @param {Boolean} anim
31243         */
31244         "beforeexpand" : true,
31245         /**
31246         * @event beforecollapse
31247         * Fires before this node is collapsed, return false to cancel.
31248         * @param {Node} this This node
31249         * @param {Boolean} deep
31250         * @param {Boolean} anim
31251         */
31252         "beforecollapse" : true,
31253         /**
31254         * @event expand
31255         * Fires when this node is expanded
31256         * @param {Node} this This node
31257         */
31258         "expand" : true,
31259         /**
31260         * @event disabledchange
31261         * Fires when the disabled status of this node changes
31262         * @param {Node} this This node
31263         * @param {Boolean} disabled
31264         */
31265         "disabledchange" : true,
31266         /**
31267         * @event collapse
31268         * Fires when this node is collapsed
31269         * @param {Node} this This node
31270         */
31271         "collapse" : true,
31272         /**
31273         * @event beforeclick
31274         * Fires before click processing. Return false to cancel the default action.
31275         * @param {Node} this This node
31276         * @param {Roo.EventObject} e The event object
31277         */
31278         "beforeclick":true,
31279         /**
31280         * @event checkchange
31281         * Fires when a node with a checkbox's checked property changes
31282         * @param {Node} this This node
31283         * @param {Boolean} checked
31284         */
31285         "checkchange":true,
31286         /**
31287         * @event click
31288         * Fires when this node is clicked
31289         * @param {Node} this This node
31290         * @param {Roo.EventObject} e The event object
31291         */
31292         "click":true,
31293         /**
31294         * @event dblclick
31295         * Fires when this node is double clicked
31296         * @param {Node} this This node
31297         * @param {Roo.EventObject} e The event object
31298         */
31299         "dblclick":true,
31300         /**
31301         * @event contextmenu
31302         * Fires when this node is right clicked
31303         * @param {Node} this This node
31304         * @param {Roo.EventObject} e The event object
31305         */
31306         "contextmenu":true,
31307         /**
31308         * @event beforechildrenrendered
31309         * Fires right before the child nodes for this node are rendered
31310         * @param {Node} this This node
31311         */
31312         "beforechildrenrendered":true
31313     });
31314
31315     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31316
31317     /**
31318      * Read-only. The UI for this node
31319      * @type TreeNodeUI
31320      */
31321     this.ui = new uiClass(this);
31322 };
31323 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31324     preventHScroll: true,
31325     /**
31326      * Returns true if this node is expanded
31327      * @return {Boolean}
31328      */
31329     isExpanded : function(){
31330         return this.expanded;
31331     },
31332
31333     /**
31334      * Returns the UI object for this node
31335      * @return {TreeNodeUI}
31336      */
31337     getUI : function(){
31338         return this.ui;
31339     },
31340
31341     // private override
31342     setFirstChild : function(node){
31343         var of = this.firstChild;
31344         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31345         if(this.childrenRendered && of && node != of){
31346             of.renderIndent(true, true);
31347         }
31348         if(this.rendered){
31349             this.renderIndent(true, true);
31350         }
31351     },
31352
31353     // private override
31354     setLastChild : function(node){
31355         var ol = this.lastChild;
31356         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31357         if(this.childrenRendered && ol && node != ol){
31358             ol.renderIndent(true, true);
31359         }
31360         if(this.rendered){
31361             this.renderIndent(true, true);
31362         }
31363     },
31364
31365     // these methods are overridden to provide lazy rendering support
31366     // private override
31367     appendChild : function(){
31368         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31369         if(node && this.childrenRendered){
31370             node.render();
31371         }
31372         this.ui.updateExpandIcon();
31373         return node;
31374     },
31375
31376     // private override
31377     removeChild : function(node){
31378         this.ownerTree.getSelectionModel().unselect(node);
31379         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31380         // if it's been rendered remove dom node
31381         if(this.childrenRendered){
31382             node.ui.remove();
31383         }
31384         if(this.childNodes.length < 1){
31385             this.collapse(false, false);
31386         }else{
31387             this.ui.updateExpandIcon();
31388         }
31389         if(!this.firstChild) {
31390             this.childrenRendered = false;
31391         }
31392         return node;
31393     },
31394
31395     // private override
31396     insertBefore : function(node, refNode){
31397         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31398         if(newNode && refNode && this.childrenRendered){
31399             node.render();
31400         }
31401         this.ui.updateExpandIcon();
31402         return newNode;
31403     },
31404
31405     /**
31406      * Sets the text for this node
31407      * @param {String} text
31408      */
31409     setText : function(text){
31410         var oldText = this.text;
31411         this.text = text;
31412         this.attributes.text = text;
31413         if(this.rendered){ // event without subscribing
31414             this.ui.onTextChange(this, text, oldText);
31415         }
31416         this.fireEvent("textchange", this, text, oldText);
31417     },
31418
31419     /**
31420      * Triggers selection of this node
31421      */
31422     select : function(){
31423         this.getOwnerTree().getSelectionModel().select(this);
31424     },
31425
31426     /**
31427      * Triggers deselection of this node
31428      */
31429     unselect : function(){
31430         this.getOwnerTree().getSelectionModel().unselect(this);
31431     },
31432
31433     /**
31434      * Returns true if this node is selected
31435      * @return {Boolean}
31436      */
31437     isSelected : function(){
31438         return this.getOwnerTree().getSelectionModel().isSelected(this);
31439     },
31440
31441     /**
31442      * Expand this node.
31443      * @param {Boolean} deep (optional) True to expand all children as well
31444      * @param {Boolean} anim (optional) false to cancel the default animation
31445      * @param {Function} callback (optional) A callback to be called when
31446      * expanding this node completes (does not wait for deep expand to complete).
31447      * Called with 1 parameter, this node.
31448      */
31449     expand : function(deep, anim, callback){
31450         if(!this.expanded){
31451             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31452                 return;
31453             }
31454             if(!this.childrenRendered){
31455                 this.renderChildren();
31456             }
31457             this.expanded = true;
31458             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31459                 this.ui.animExpand(function(){
31460                     this.fireEvent("expand", this);
31461                     if(typeof callback == "function"){
31462                         callback(this);
31463                     }
31464                     if(deep === true){
31465                         this.expandChildNodes(true);
31466                     }
31467                 }.createDelegate(this));
31468                 return;
31469             }else{
31470                 this.ui.expand();
31471                 this.fireEvent("expand", this);
31472                 if(typeof callback == "function"){
31473                     callback(this);
31474                 }
31475             }
31476         }else{
31477            if(typeof callback == "function"){
31478                callback(this);
31479            }
31480         }
31481         if(deep === true){
31482             this.expandChildNodes(true);
31483         }
31484     },
31485
31486     isHiddenRoot : function(){
31487         return this.isRoot && !this.getOwnerTree().rootVisible;
31488     },
31489
31490     /**
31491      * Collapse this node.
31492      * @param {Boolean} deep (optional) True to collapse all children as well
31493      * @param {Boolean} anim (optional) false to cancel the default animation
31494      */
31495     collapse : function(deep, anim){
31496         if(this.expanded && !this.isHiddenRoot()){
31497             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31498                 return;
31499             }
31500             this.expanded = false;
31501             if((this.getOwnerTree().animate && anim !== false) || anim){
31502                 this.ui.animCollapse(function(){
31503                     this.fireEvent("collapse", this);
31504                     if(deep === true){
31505                         this.collapseChildNodes(true);
31506                     }
31507                 }.createDelegate(this));
31508                 return;
31509             }else{
31510                 this.ui.collapse();
31511                 this.fireEvent("collapse", this);
31512             }
31513         }
31514         if(deep === true){
31515             var cs = this.childNodes;
31516             for(var i = 0, len = cs.length; i < len; i++) {
31517                 cs[i].collapse(true, false);
31518             }
31519         }
31520     },
31521
31522     // private
31523     delayedExpand : function(delay){
31524         if(!this.expandProcId){
31525             this.expandProcId = this.expand.defer(delay, this);
31526         }
31527     },
31528
31529     // private
31530     cancelExpand : function(){
31531         if(this.expandProcId){
31532             clearTimeout(this.expandProcId);
31533         }
31534         this.expandProcId = false;
31535     },
31536
31537     /**
31538      * Toggles expanded/collapsed state of the node
31539      */
31540     toggle : function(){
31541         if(this.expanded){
31542             this.collapse();
31543         }else{
31544             this.expand();
31545         }
31546     },
31547
31548     /**
31549      * Ensures all parent nodes are expanded
31550      */
31551     ensureVisible : function(callback){
31552         var tree = this.getOwnerTree();
31553         tree.expandPath(this.parentNode.getPath(), false, function(){
31554             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31555             Roo.callback(callback);
31556         }.createDelegate(this));
31557     },
31558
31559     /**
31560      * Expand all child nodes
31561      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31562      */
31563     expandChildNodes : function(deep){
31564         var cs = this.childNodes;
31565         for(var i = 0, len = cs.length; i < len; i++) {
31566                 cs[i].expand(deep);
31567         }
31568     },
31569
31570     /**
31571      * Collapse all child nodes
31572      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31573      */
31574     collapseChildNodes : function(deep){
31575         var cs = this.childNodes;
31576         for(var i = 0, len = cs.length; i < len; i++) {
31577                 cs[i].collapse(deep);
31578         }
31579     },
31580
31581     /**
31582      * Disables this node
31583      */
31584     disable : function(){
31585         this.disabled = true;
31586         this.unselect();
31587         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31588             this.ui.onDisableChange(this, true);
31589         }
31590         this.fireEvent("disabledchange", this, true);
31591     },
31592
31593     /**
31594      * Enables this node
31595      */
31596     enable : function(){
31597         this.disabled = false;
31598         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31599             this.ui.onDisableChange(this, false);
31600         }
31601         this.fireEvent("disabledchange", this, false);
31602     },
31603
31604     // private
31605     renderChildren : function(suppressEvent){
31606         if(suppressEvent !== false){
31607             this.fireEvent("beforechildrenrendered", this);
31608         }
31609         var cs = this.childNodes;
31610         for(var i = 0, len = cs.length; i < len; i++){
31611             cs[i].render(true);
31612         }
31613         this.childrenRendered = true;
31614     },
31615
31616     // private
31617     sort : function(fn, scope){
31618         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31619         if(this.childrenRendered){
31620             var cs = this.childNodes;
31621             for(var i = 0, len = cs.length; i < len; i++){
31622                 cs[i].render(true);
31623             }
31624         }
31625     },
31626
31627     // private
31628     render : function(bulkRender){
31629         this.ui.render(bulkRender);
31630         if(!this.rendered){
31631             this.rendered = true;
31632             if(this.expanded){
31633                 this.expanded = false;
31634                 this.expand(false, false);
31635             }
31636         }
31637     },
31638
31639     // private
31640     renderIndent : function(deep, refresh){
31641         if(refresh){
31642             this.ui.childIndent = null;
31643         }
31644         this.ui.renderIndent();
31645         if(deep === true && this.childrenRendered){
31646             var cs = this.childNodes;
31647             for(var i = 0, len = cs.length; i < len; i++){
31648                 cs[i].renderIndent(true, refresh);
31649             }
31650         }
31651     }
31652 });/*
31653  * Based on:
31654  * Ext JS Library 1.1.1
31655  * Copyright(c) 2006-2007, Ext JS, LLC.
31656  *
31657  * Originally Released Under LGPL - original licence link has changed is not relivant.
31658  *
31659  * Fork - LGPL
31660  * <script type="text/javascript">
31661  */
31662  
31663 /**
31664  * @class Roo.tree.AsyncTreeNode
31665  * @extends Roo.tree.TreeNode
31666  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31667  * @constructor
31668  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31669  */
31670  Roo.tree.AsyncTreeNode = function(config){
31671     this.loaded = false;
31672     this.loading = false;
31673     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31674     /**
31675     * @event beforeload
31676     * Fires before this node is loaded, return false to cancel
31677     * @param {Node} this This node
31678     */
31679     this.addEvents({'beforeload':true, 'load': true});
31680     /**
31681     * @event load
31682     * Fires when this node is loaded
31683     * @param {Node} this This node
31684     */
31685     /**
31686      * The loader used by this node (defaults to using the tree's defined loader)
31687      * @type TreeLoader
31688      * @property loader
31689      */
31690 };
31691 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31692     expand : function(deep, anim, callback){
31693         if(this.loading){ // if an async load is already running, waiting til it's done
31694             var timer;
31695             var f = function(){
31696                 if(!this.loading){ // done loading
31697                     clearInterval(timer);
31698                     this.expand(deep, anim, callback);
31699                 }
31700             }.createDelegate(this);
31701             timer = setInterval(f, 200);
31702             return;
31703         }
31704         if(!this.loaded){
31705             if(this.fireEvent("beforeload", this) === false){
31706                 return;
31707             }
31708             this.loading = true;
31709             this.ui.beforeLoad(this);
31710             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31711             if(loader){
31712                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31713                 return;
31714             }
31715         }
31716         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31717     },
31718     
31719     /**
31720      * Returns true if this node is currently loading
31721      * @return {Boolean}
31722      */
31723     isLoading : function(){
31724         return this.loading;  
31725     },
31726     
31727     loadComplete : function(deep, anim, callback){
31728         this.loading = false;
31729         this.loaded = true;
31730         this.ui.afterLoad(this);
31731         this.fireEvent("load", this);
31732         this.expand(deep, anim, callback);
31733     },
31734     
31735     /**
31736      * Returns true if this node has been loaded
31737      * @return {Boolean}
31738      */
31739     isLoaded : function(){
31740         return this.loaded;
31741     },
31742     
31743     hasChildNodes : function(){
31744         if(!this.isLeaf() && !this.loaded){
31745             return true;
31746         }else{
31747             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31748         }
31749     },
31750
31751     /**
31752      * Trigger a reload for this node
31753      * @param {Function} callback
31754      */
31755     reload : function(callback){
31756         this.collapse(false, false);
31757         while(this.firstChild){
31758             this.removeChild(this.firstChild);
31759         }
31760         this.childrenRendered = false;
31761         this.loaded = false;
31762         if(this.isHiddenRoot()){
31763             this.expanded = false;
31764         }
31765         this.expand(false, false, callback);
31766     }
31767 });/*
31768  * Based on:
31769  * Ext JS Library 1.1.1
31770  * Copyright(c) 2006-2007, Ext JS, LLC.
31771  *
31772  * Originally Released Under LGPL - original licence link has changed is not relivant.
31773  *
31774  * Fork - LGPL
31775  * <script type="text/javascript">
31776  */
31777  
31778 /**
31779  * @class Roo.tree.TreeNodeUI
31780  * @constructor
31781  * @param {Object} node The node to render
31782  * The TreeNode UI implementation is separate from the
31783  * tree implementation. Unless you are customizing the tree UI,
31784  * you should never have to use this directly.
31785  */
31786 Roo.tree.TreeNodeUI = function(node){
31787     this.node = node;
31788     this.rendered = false;
31789     this.animating = false;
31790     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31791 };
31792
31793 Roo.tree.TreeNodeUI.prototype = {
31794     removeChild : function(node){
31795         if(this.rendered){
31796             this.ctNode.removeChild(node.ui.getEl());
31797         }
31798     },
31799
31800     beforeLoad : function(){
31801          this.addClass("x-tree-node-loading");
31802     },
31803
31804     afterLoad : function(){
31805          this.removeClass("x-tree-node-loading");
31806     },
31807
31808     onTextChange : function(node, text, oldText){
31809         if(this.rendered){
31810             this.textNode.innerHTML = text;
31811         }
31812     },
31813
31814     onDisableChange : function(node, state){
31815         this.disabled = state;
31816         if(state){
31817             this.addClass("x-tree-node-disabled");
31818         }else{
31819             this.removeClass("x-tree-node-disabled");
31820         }
31821     },
31822
31823     onSelectedChange : function(state){
31824         if(state){
31825             this.focus();
31826             this.addClass("x-tree-selected");
31827         }else{
31828             //this.blur();
31829             this.removeClass("x-tree-selected");
31830         }
31831     },
31832
31833     onMove : function(tree, node, oldParent, newParent, index, refNode){
31834         this.childIndent = null;
31835         if(this.rendered){
31836             var targetNode = newParent.ui.getContainer();
31837             if(!targetNode){//target not rendered
31838                 this.holder = document.createElement("div");
31839                 this.holder.appendChild(this.wrap);
31840                 return;
31841             }
31842             var insertBefore = refNode ? refNode.ui.getEl() : null;
31843             if(insertBefore){
31844                 targetNode.insertBefore(this.wrap, insertBefore);
31845             }else{
31846                 targetNode.appendChild(this.wrap);
31847             }
31848             this.node.renderIndent(true);
31849         }
31850     },
31851
31852     addClass : function(cls){
31853         if(this.elNode){
31854             Roo.fly(this.elNode).addClass(cls);
31855         }
31856     },
31857
31858     removeClass : function(cls){
31859         if(this.elNode){
31860             Roo.fly(this.elNode).removeClass(cls);
31861         }
31862     },
31863
31864     remove : function(){
31865         if(this.rendered){
31866             this.holder = document.createElement("div");
31867             this.holder.appendChild(this.wrap);
31868         }
31869     },
31870
31871     fireEvent : function(){
31872         return this.node.fireEvent.apply(this.node, arguments);
31873     },
31874
31875     initEvents : function(){
31876         this.node.on("move", this.onMove, this);
31877         var E = Roo.EventManager;
31878         var a = this.anchor;
31879
31880         var el = Roo.fly(a, '_treeui');
31881
31882         if(Roo.isOpera){ // opera render bug ignores the CSS
31883             el.setStyle("text-decoration", "none");
31884         }
31885
31886         el.on("click", this.onClick, this);
31887         el.on("dblclick", this.onDblClick, this);
31888
31889         if(this.checkbox){
31890             Roo.EventManager.on(this.checkbox,
31891                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
31892         }
31893
31894         el.on("contextmenu", this.onContextMenu, this);
31895
31896         var icon = Roo.fly(this.iconNode);
31897         icon.on("click", this.onClick, this);
31898         icon.on("dblclick", this.onDblClick, this);
31899         icon.on("contextmenu", this.onContextMenu, this);
31900         E.on(this.ecNode, "click", this.ecClick, this, true);
31901
31902         if(this.node.disabled){
31903             this.addClass("x-tree-node-disabled");
31904         }
31905         if(this.node.hidden){
31906             this.addClass("x-tree-node-disabled");
31907         }
31908         var ot = this.node.getOwnerTree();
31909         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
31910         if(dd && (!this.node.isRoot || ot.rootVisible)){
31911             Roo.dd.Registry.register(this.elNode, {
31912                 node: this.node,
31913                 handles: this.getDDHandles(),
31914                 isHandle: false
31915             });
31916         }
31917     },
31918
31919     getDDHandles : function(){
31920         return [this.iconNode, this.textNode];
31921     },
31922
31923     hide : function(){
31924         if(this.rendered){
31925             this.wrap.style.display = "none";
31926         }
31927     },
31928
31929     show : function(){
31930         if(this.rendered){
31931             this.wrap.style.display = "";
31932         }
31933     },
31934
31935     onContextMenu : function(e){
31936         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
31937             e.preventDefault();
31938             this.focus();
31939             this.fireEvent("contextmenu", this.node, e);
31940         }
31941     },
31942
31943     onClick : function(e){
31944         if(this.dropping){
31945             e.stopEvent();
31946             return;
31947         }
31948         if(this.fireEvent("beforeclick", this.node, e) !== false){
31949             if(!this.disabled && this.node.attributes.href){
31950                 this.fireEvent("click", this.node, e);
31951                 return;
31952             }
31953             e.preventDefault();
31954             if(this.disabled){
31955                 return;
31956             }
31957
31958             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
31959                 this.node.toggle();
31960             }
31961
31962             this.fireEvent("click", this.node, e);
31963         }else{
31964             e.stopEvent();
31965         }
31966     },
31967
31968     onDblClick : function(e){
31969         e.preventDefault();
31970         if(this.disabled){
31971             return;
31972         }
31973         if(this.checkbox){
31974             this.toggleCheck();
31975         }
31976         if(!this.animating && this.node.hasChildNodes()){
31977             this.node.toggle();
31978         }
31979         this.fireEvent("dblclick", this.node, e);
31980     },
31981
31982     onCheckChange : function(){
31983         var checked = this.checkbox.checked;
31984         this.node.attributes.checked = checked;
31985         this.fireEvent('checkchange', this.node, checked);
31986     },
31987
31988     ecClick : function(e){
31989         if(!this.animating && this.node.hasChildNodes()){
31990             this.node.toggle();
31991         }
31992     },
31993
31994     startDrop : function(){
31995         this.dropping = true;
31996     },
31997
31998     // delayed drop so the click event doesn't get fired on a drop
31999     endDrop : function(){
32000        setTimeout(function(){
32001            this.dropping = false;
32002        }.createDelegate(this), 50);
32003     },
32004
32005     expand : function(){
32006         this.updateExpandIcon();
32007         this.ctNode.style.display = "";
32008     },
32009
32010     focus : function(){
32011         if(!this.node.preventHScroll){
32012             try{this.anchor.focus();
32013             }catch(e){}
32014         }else if(!Roo.isIE){
32015             try{
32016                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32017                 var l = noscroll.scrollLeft;
32018                 this.anchor.focus();
32019                 noscroll.scrollLeft = l;
32020             }catch(e){}
32021         }
32022     },
32023
32024     toggleCheck : function(value){
32025         var cb = this.checkbox;
32026         if(cb){
32027             cb.checked = (value === undefined ? !cb.checked : value);
32028         }
32029     },
32030
32031     blur : function(){
32032         try{
32033             this.anchor.blur();
32034         }catch(e){}
32035     },
32036
32037     animExpand : function(callback){
32038         var ct = Roo.get(this.ctNode);
32039         ct.stopFx();
32040         if(!this.node.hasChildNodes()){
32041             this.updateExpandIcon();
32042             this.ctNode.style.display = "";
32043             Roo.callback(callback);
32044             return;
32045         }
32046         this.animating = true;
32047         this.updateExpandIcon();
32048
32049         ct.slideIn('t', {
32050            callback : function(){
32051                this.animating = false;
32052                Roo.callback(callback);
32053             },
32054             scope: this,
32055             duration: this.node.ownerTree.duration || .25
32056         });
32057     },
32058
32059     highlight : function(){
32060         var tree = this.node.getOwnerTree();
32061         Roo.fly(this.wrap).highlight(
32062             tree.hlColor || "C3DAF9",
32063             {endColor: tree.hlBaseColor}
32064         );
32065     },
32066
32067     collapse : function(){
32068         this.updateExpandIcon();
32069         this.ctNode.style.display = "none";
32070     },
32071
32072     animCollapse : function(callback){
32073         var ct = Roo.get(this.ctNode);
32074         ct.enableDisplayMode('block');
32075         ct.stopFx();
32076
32077         this.animating = true;
32078         this.updateExpandIcon();
32079
32080         ct.slideOut('t', {
32081             callback : function(){
32082                this.animating = false;
32083                Roo.callback(callback);
32084             },
32085             scope: this,
32086             duration: this.node.ownerTree.duration || .25
32087         });
32088     },
32089
32090     getContainer : function(){
32091         return this.ctNode;
32092     },
32093
32094     getEl : function(){
32095         return this.wrap;
32096     },
32097
32098     appendDDGhost : function(ghostNode){
32099         ghostNode.appendChild(this.elNode.cloneNode(true));
32100     },
32101
32102     getDDRepairXY : function(){
32103         return Roo.lib.Dom.getXY(this.iconNode);
32104     },
32105
32106     onRender : function(){
32107         this.render();
32108     },
32109
32110     render : function(bulkRender){
32111         var n = this.node, a = n.attributes;
32112         var targetNode = n.parentNode ?
32113               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32114
32115         if(!this.rendered){
32116             this.rendered = true;
32117
32118             this.renderElements(n, a, targetNode, bulkRender);
32119
32120             if(a.qtip){
32121                if(this.textNode.setAttributeNS){
32122                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32123                    if(a.qtipTitle){
32124                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32125                    }
32126                }else{
32127                    this.textNode.setAttribute("ext:qtip", a.qtip);
32128                    if(a.qtipTitle){
32129                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32130                    }
32131                }
32132             }else if(a.qtipCfg){
32133                 a.qtipCfg.target = Roo.id(this.textNode);
32134                 Roo.QuickTips.register(a.qtipCfg);
32135             }
32136             this.initEvents();
32137             if(!this.node.expanded){
32138                 this.updateExpandIcon();
32139             }
32140         }else{
32141             if(bulkRender === true) {
32142                 targetNode.appendChild(this.wrap);
32143             }
32144         }
32145     },
32146
32147     renderElements : function(n, a, targetNode, bulkRender){
32148         // add some indent caching, this helps performance when rendering a large tree
32149         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32150         var t = n.getOwnerTree();
32151         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32152         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32153         var cb = typeof a.checked == 'boolean';
32154         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32155         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32156             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32157             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32158             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32159             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32160             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32161              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32162                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32163             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32164             "</li>"];
32165
32166         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32167             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32168                                 n.nextSibling.ui.getEl(), buf.join(""));
32169         }else{
32170             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32171         }
32172
32173         this.elNode = this.wrap.childNodes[0];
32174         this.ctNode = this.wrap.childNodes[1];
32175         var cs = this.elNode.childNodes;
32176         this.indentNode = cs[0];
32177         this.ecNode = cs[1];
32178         this.iconNode = cs[2];
32179         var index = 3;
32180         if(cb){
32181             this.checkbox = cs[3];
32182             index++;
32183         }
32184         this.anchor = cs[index];
32185         this.textNode = cs[index].firstChild;
32186     },
32187
32188     getAnchor : function(){
32189         return this.anchor;
32190     },
32191
32192     getTextEl : function(){
32193         return this.textNode;
32194     },
32195
32196     getIconEl : function(){
32197         return this.iconNode;
32198     },
32199
32200     isChecked : function(){
32201         return this.checkbox ? this.checkbox.checked : false;
32202     },
32203
32204     updateExpandIcon : function(){
32205         if(this.rendered){
32206             var n = this.node, c1, c2;
32207             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32208             var hasChild = n.hasChildNodes();
32209             if(hasChild){
32210                 if(n.expanded){
32211                     cls += "-minus";
32212                     c1 = "x-tree-node-collapsed";
32213                     c2 = "x-tree-node-expanded";
32214                 }else{
32215                     cls += "-plus";
32216                     c1 = "x-tree-node-expanded";
32217                     c2 = "x-tree-node-collapsed";
32218                 }
32219                 if(this.wasLeaf){
32220                     this.removeClass("x-tree-node-leaf");
32221                     this.wasLeaf = false;
32222                 }
32223                 if(this.c1 != c1 || this.c2 != c2){
32224                     Roo.fly(this.elNode).replaceClass(c1, c2);
32225                     this.c1 = c1; this.c2 = c2;
32226                 }
32227             }else{
32228                 if(!this.wasLeaf){
32229                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32230                     delete this.c1;
32231                     delete this.c2;
32232                     this.wasLeaf = true;
32233                 }
32234             }
32235             var ecc = "x-tree-ec-icon "+cls;
32236             if(this.ecc != ecc){
32237                 this.ecNode.className = ecc;
32238                 this.ecc = ecc;
32239             }
32240         }
32241     },
32242
32243     getChildIndent : function(){
32244         if(!this.childIndent){
32245             var buf = [];
32246             var p = this.node;
32247             while(p){
32248                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32249                     if(!p.isLast()) {
32250                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32251                     } else {
32252                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32253                     }
32254                 }
32255                 p = p.parentNode;
32256             }
32257             this.childIndent = buf.join("");
32258         }
32259         return this.childIndent;
32260     },
32261
32262     renderIndent : function(){
32263         if(this.rendered){
32264             var indent = "";
32265             var p = this.node.parentNode;
32266             if(p){
32267                 indent = p.ui.getChildIndent();
32268             }
32269             if(this.indentMarkup != indent){ // don't rerender if not required
32270                 this.indentNode.innerHTML = indent;
32271                 this.indentMarkup = indent;
32272             }
32273             this.updateExpandIcon();
32274         }
32275     }
32276 };
32277
32278 Roo.tree.RootTreeNodeUI = function(){
32279     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32280 };
32281 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32282     render : function(){
32283         if(!this.rendered){
32284             var targetNode = this.node.ownerTree.innerCt.dom;
32285             this.node.expanded = true;
32286             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32287             this.wrap = this.ctNode = targetNode.firstChild;
32288         }
32289     },
32290     collapse : function(){
32291     },
32292     expand : function(){
32293     }
32294 });/*
32295  * Based on:
32296  * Ext JS Library 1.1.1
32297  * Copyright(c) 2006-2007, Ext JS, LLC.
32298  *
32299  * Originally Released Under LGPL - original licence link has changed is not relivant.
32300  *
32301  * Fork - LGPL
32302  * <script type="text/javascript">
32303  */
32304 /**
32305  * @class Roo.tree.TreeLoader
32306  * @extends Roo.util.Observable
32307  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32308  * nodes from a specified URL. The response must be a javascript Array definition
32309  * who's elements are node definition objects. eg:
32310  * <pre><code>
32311    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32312     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32313 </code></pre>
32314  * <br><br>
32315  * A server request is sent, and child nodes are loaded only when a node is expanded.
32316  * The loading node's id is passed to the server under the parameter name "node" to
32317  * enable the server to produce the correct child nodes.
32318  * <br><br>
32319  * To pass extra parameters, an event handler may be attached to the "beforeload"
32320  * event, and the parameters specified in the TreeLoader's baseParams property:
32321  * <pre><code>
32322     myTreeLoader.on("beforeload", function(treeLoader, node) {
32323         this.baseParams.category = node.attributes.category;
32324     }, this);
32325 </code></pre><
32326  * This would pass an HTTP parameter called "category" to the server containing
32327  * the value of the Node's "category" attribute.
32328  * @constructor
32329  * Creates a new Treeloader.
32330  * @param {Object} config A config object containing config properties.
32331  */
32332 Roo.tree.TreeLoader = function(config){
32333     this.baseParams = {};
32334     this.requestMethod = "POST";
32335     Roo.apply(this, config);
32336
32337     this.addEvents({
32338     
32339         /**
32340          * @event beforeload
32341          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32342          * @param {Object} This TreeLoader object.
32343          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32344          * @param {Object} callback The callback function specified in the {@link #load} call.
32345          */
32346         beforeload : true,
32347         /**
32348          * @event load
32349          * Fires when the node has been successfuly loaded.
32350          * @param {Object} This TreeLoader object.
32351          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32352          * @param {Object} response The response object containing the data from the server.
32353          */
32354         load : true,
32355         /**
32356          * @event loadexception
32357          * Fires if the network request failed.
32358          * @param {Object} This TreeLoader object.
32359          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32360          * @param {Object} response The response object containing the data from the server.
32361          */
32362         loadexception : true,
32363         /**
32364          * @event create
32365          * Fires before a node is created, enabling you to return custom Node types 
32366          * @param {Object} This TreeLoader object.
32367          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32368          */
32369         create : true
32370     });
32371
32372     Roo.tree.TreeLoader.superclass.constructor.call(this);
32373 };
32374
32375 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32376     /**
32377     * @cfg {String} dataUrl The URL from which to request a Json string which
32378     * specifies an array of node definition object representing the child nodes
32379     * to be loaded.
32380     */
32381     /**
32382     * @cfg {Object} baseParams (optional) An object containing properties which
32383     * specify HTTP parameters to be passed to each request for child nodes.
32384     */
32385     /**
32386     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32387     * created by this loader. If the attributes sent by the server have an attribute in this object,
32388     * they take priority.
32389     */
32390     /**
32391     * @cfg {Object} uiProviders (optional) An object containing properties which
32392     * 
32393     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32394     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32395     * <i>uiProvider</i> attribute of a returned child node is a string rather
32396     * than a reference to a TreeNodeUI implementation, this that string value
32397     * is used as a property name in the uiProviders object. You can define the provider named
32398     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32399     */
32400     uiProviders : {},
32401
32402     /**
32403     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32404     * child nodes before loading.
32405     */
32406     clearOnLoad : true,
32407
32408     /**
32409     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32410     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32411     * Grid query { data : [ .....] }
32412     */
32413     
32414     root : false,
32415      /**
32416     * @cfg {String} queryParam (optional) 
32417     * Name of the query as it will be passed on the querystring (defaults to 'node')
32418     * eg. the request will be ?node=[id]
32419     */
32420     
32421     
32422     queryParam: false,
32423     
32424     /**
32425      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32426      * This is called automatically when a node is expanded, but may be used to reload
32427      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32428      * @param {Roo.tree.TreeNode} node
32429      * @param {Function} callback
32430      */
32431     load : function(node, callback){
32432         if(this.clearOnLoad){
32433             while(node.firstChild){
32434                 node.removeChild(node.firstChild);
32435             }
32436         }
32437         if(node.attributes.children){ // preloaded json children
32438             var cs = node.attributes.children;
32439             for(var i = 0, len = cs.length; i < len; i++){
32440                 node.appendChild(this.createNode(cs[i]));
32441             }
32442             if(typeof callback == "function"){
32443                 callback();
32444             }
32445         }else if(this.dataUrl){
32446             this.requestData(node, callback);
32447         }
32448     },
32449
32450     getParams: function(node){
32451         var buf = [], bp = this.baseParams;
32452         for(var key in bp){
32453             if(typeof bp[key] != "function"){
32454                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32455             }
32456         }
32457         var n = this.queryParam === false ? 'node' : this.queryParam;
32458         buf.push(n + "=", encodeURIComponent(node.id));
32459         return buf.join("");
32460     },
32461
32462     requestData : function(node, callback){
32463         if(this.fireEvent("beforeload", this, node, callback) !== false){
32464             this.transId = Roo.Ajax.request({
32465                 method:this.requestMethod,
32466                 url: this.dataUrl||this.url,
32467                 success: this.handleResponse,
32468                 failure: this.handleFailure,
32469                 scope: this,
32470                 argument: {callback: callback, node: node},
32471                 params: this.getParams(node)
32472             });
32473         }else{
32474             // if the load is cancelled, make sure we notify
32475             // the node that we are done
32476             if(typeof callback == "function"){
32477                 callback();
32478             }
32479         }
32480     },
32481
32482     isLoading : function(){
32483         return this.transId ? true : false;
32484     },
32485
32486     abort : function(){
32487         if(this.isLoading()){
32488             Roo.Ajax.abort(this.transId);
32489         }
32490     },
32491
32492     // private
32493     createNode : function(attr){
32494         // apply baseAttrs, nice idea Corey!
32495         if(this.baseAttrs){
32496             Roo.applyIf(attr, this.baseAttrs);
32497         }
32498         if(this.applyLoader !== false){
32499             attr.loader = this;
32500         }
32501         // uiProvider = depreciated..
32502         
32503         if(typeof(attr.uiProvider) == 'string'){
32504            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32505                 /**  eval:var:attr */ eval(attr.uiProvider);
32506         }
32507         if(typeof(this.uiProviders['default']) != 'undefined') {
32508             attr.uiProvider = this.uiProviders['default'];
32509         }
32510         
32511         this.fireEvent('create', this, attr);
32512         
32513         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32514         return(attr.leaf ?
32515                         new Roo.tree.TreeNode(attr) :
32516                         new Roo.tree.AsyncTreeNode(attr));
32517     },
32518
32519     processResponse : function(response, node, callback){
32520         var json = response.responseText;
32521         try {
32522             
32523             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32524             if (this.root !== false) {
32525                 o = o[this.root];
32526             }
32527             
32528             for(var i = 0, len = o.length; i < len; i++){
32529                 var n = this.createNode(o[i]);
32530                 if(n){
32531                     node.appendChild(n);
32532                 }
32533             }
32534             if(typeof callback == "function"){
32535                 callback(this, node);
32536             }
32537         }catch(e){
32538             this.handleFailure(response);
32539         }
32540     },
32541
32542     handleResponse : function(response){
32543         this.transId = false;
32544         var a = response.argument;
32545         this.processResponse(response, a.node, a.callback);
32546         this.fireEvent("load", this, a.node, response);
32547     },
32548
32549     handleFailure : function(response){
32550         this.transId = false;
32551         var a = response.argument;
32552         this.fireEvent("loadexception", this, a.node, response);
32553         if(typeof a.callback == "function"){
32554             a.callback(this, a.node);
32555         }
32556     }
32557 });/*
32558  * Based on:
32559  * Ext JS Library 1.1.1
32560  * Copyright(c) 2006-2007, Ext JS, LLC.
32561  *
32562  * Originally Released Under LGPL - original licence link has changed is not relivant.
32563  *
32564  * Fork - LGPL
32565  * <script type="text/javascript">
32566  */
32567
32568 /**
32569 * @class Roo.tree.TreeFilter
32570 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32571 * @param {TreePanel} tree
32572 * @param {Object} config (optional)
32573  */
32574 Roo.tree.TreeFilter = function(tree, config){
32575     this.tree = tree;
32576     this.filtered = {};
32577     Roo.apply(this, config);
32578 };
32579
32580 Roo.tree.TreeFilter.prototype = {
32581     clearBlank:false,
32582     reverse:false,
32583     autoClear:false,
32584     remove:false,
32585
32586      /**
32587      * Filter the data by a specific attribute.
32588      * @param {String/RegExp} value Either string that the attribute value
32589      * should start with or a RegExp to test against the attribute
32590      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32591      * @param {TreeNode} startNode (optional) The node to start the filter at.
32592      */
32593     filter : function(value, attr, startNode){
32594         attr = attr || "text";
32595         var f;
32596         if(typeof value == "string"){
32597             var vlen = value.length;
32598             // auto clear empty filter
32599             if(vlen == 0 && this.clearBlank){
32600                 this.clear();
32601                 return;
32602             }
32603             value = value.toLowerCase();
32604             f = function(n){
32605                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32606             };
32607         }else if(value.exec){ // regex?
32608             f = function(n){
32609                 return value.test(n.attributes[attr]);
32610             };
32611         }else{
32612             throw 'Illegal filter type, must be string or regex';
32613         }
32614         this.filterBy(f, null, startNode);
32615         },
32616
32617     /**
32618      * Filter by a function. The passed function will be called with each
32619      * node in the tree (or from the startNode). If the function returns true, the node is kept
32620      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32621      * @param {Function} fn The filter function
32622      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32623      */
32624     filterBy : function(fn, scope, startNode){
32625         startNode = startNode || this.tree.root;
32626         if(this.autoClear){
32627             this.clear();
32628         }
32629         var af = this.filtered, rv = this.reverse;
32630         var f = function(n){
32631             if(n == startNode){
32632                 return true;
32633             }
32634             if(af[n.id]){
32635                 return false;
32636             }
32637             var m = fn.call(scope || n, n);
32638             if(!m || rv){
32639                 af[n.id] = n;
32640                 n.ui.hide();
32641                 return false;
32642             }
32643             return true;
32644         };
32645         startNode.cascade(f);
32646         if(this.remove){
32647            for(var id in af){
32648                if(typeof id != "function"){
32649                    var n = af[id];
32650                    if(n && n.parentNode){
32651                        n.parentNode.removeChild(n);
32652                    }
32653                }
32654            }
32655         }
32656     },
32657
32658     /**
32659      * Clears the current filter. Note: with the "remove" option
32660      * set a filter cannot be cleared.
32661      */
32662     clear : function(){
32663         var t = this.tree;
32664         var af = this.filtered;
32665         for(var id in af){
32666             if(typeof id != "function"){
32667                 var n = af[id];
32668                 if(n){
32669                     n.ui.show();
32670                 }
32671             }
32672         }
32673         this.filtered = {};
32674     }
32675 };
32676 /*
32677  * Based on:
32678  * Ext JS Library 1.1.1
32679  * Copyright(c) 2006-2007, Ext JS, LLC.
32680  *
32681  * Originally Released Under LGPL - original licence link has changed is not relivant.
32682  *
32683  * Fork - LGPL
32684  * <script type="text/javascript">
32685  */
32686  
32687
32688 /**
32689  * @class Roo.tree.TreeSorter
32690  * Provides sorting of nodes in a TreePanel
32691  * 
32692  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32693  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32694  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32695  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32696  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32697  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32698  * @constructor
32699  * @param {TreePanel} tree
32700  * @param {Object} config
32701  */
32702 Roo.tree.TreeSorter = function(tree, config){
32703     Roo.apply(this, config);
32704     tree.on("beforechildrenrendered", this.doSort, this);
32705     tree.on("append", this.updateSort, this);
32706     tree.on("insert", this.updateSort, this);
32707     
32708     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32709     var p = this.property || "text";
32710     var sortType = this.sortType;
32711     var fs = this.folderSort;
32712     var cs = this.caseSensitive === true;
32713     var leafAttr = this.leafAttr || 'leaf';
32714
32715     this.sortFn = function(n1, n2){
32716         if(fs){
32717             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32718                 return 1;
32719             }
32720             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32721                 return -1;
32722             }
32723         }
32724         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32725         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32726         if(v1 < v2){
32727                         return dsc ? +1 : -1;
32728                 }else if(v1 > v2){
32729                         return dsc ? -1 : +1;
32730         }else{
32731                 return 0;
32732         }
32733     };
32734 };
32735
32736 Roo.tree.TreeSorter.prototype = {
32737     doSort : function(node){
32738         node.sort(this.sortFn);
32739     },
32740     
32741     compareNodes : function(n1, n2){
32742         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32743     },
32744     
32745     updateSort : function(tree, node){
32746         if(node.childrenRendered){
32747             this.doSort.defer(1, this, [node]);
32748         }
32749     }
32750 };/*
32751  * Based on:
32752  * Ext JS Library 1.1.1
32753  * Copyright(c) 2006-2007, Ext JS, LLC.
32754  *
32755  * Originally Released Under LGPL - original licence link has changed is not relivant.
32756  *
32757  * Fork - LGPL
32758  * <script type="text/javascript">
32759  */
32760
32761 if(Roo.dd.DropZone){
32762     
32763 Roo.tree.TreeDropZone = function(tree, config){
32764     this.allowParentInsert = false;
32765     this.allowContainerDrop = false;
32766     this.appendOnly = false;
32767     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32768     this.tree = tree;
32769     this.lastInsertClass = "x-tree-no-status";
32770     this.dragOverData = {};
32771 };
32772
32773 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32774     ddGroup : "TreeDD",
32775     
32776     expandDelay : 1000,
32777     
32778     expandNode : function(node){
32779         if(node.hasChildNodes() && !node.isExpanded()){
32780             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32781         }
32782     },
32783     
32784     queueExpand : function(node){
32785         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32786     },
32787     
32788     cancelExpand : function(){
32789         if(this.expandProcId){
32790             clearTimeout(this.expandProcId);
32791             this.expandProcId = false;
32792         }
32793     },
32794     
32795     isValidDropPoint : function(n, pt, dd, e, data){
32796         if(!n || !data){ return false; }
32797         var targetNode = n.node;
32798         var dropNode = data.node;
32799         // default drop rules
32800         if(!(targetNode && targetNode.isTarget && pt)){
32801             return false;
32802         }
32803         if(pt == "append" && targetNode.allowChildren === false){
32804             return false;
32805         }
32806         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32807             return false;
32808         }
32809         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32810             return false;
32811         }
32812         // reuse the object
32813         var overEvent = this.dragOverData;
32814         overEvent.tree = this.tree;
32815         overEvent.target = targetNode;
32816         overEvent.data = data;
32817         overEvent.point = pt;
32818         overEvent.source = dd;
32819         overEvent.rawEvent = e;
32820         overEvent.dropNode = dropNode;
32821         overEvent.cancel = false;  
32822         var result = this.tree.fireEvent("nodedragover", overEvent);
32823         return overEvent.cancel === false && result !== false;
32824     },
32825     
32826     getDropPoint : function(e, n, dd){
32827         var tn = n.node;
32828         if(tn.isRoot){
32829             return tn.allowChildren !== false ? "append" : false; // always append for root
32830         }
32831         var dragEl = n.ddel;
32832         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32833         var y = Roo.lib.Event.getPageY(e);
32834         var noAppend = tn.allowChildren === false || tn.isLeaf();
32835         if(this.appendOnly || tn.parentNode.allowChildren === false){
32836             return noAppend ? false : "append";
32837         }
32838         var noBelow = false;
32839         if(!this.allowParentInsert){
32840             noBelow = tn.hasChildNodes() && tn.isExpanded();
32841         }
32842         var q = (b - t) / (noAppend ? 2 : 3);
32843         if(y >= t && y < (t + q)){
32844             return "above";
32845         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
32846             return "below";
32847         }else{
32848             return "append";
32849         }
32850     },
32851     
32852     onNodeEnter : function(n, dd, e, data){
32853         this.cancelExpand();
32854     },
32855     
32856     onNodeOver : function(n, dd, e, data){
32857         var pt = this.getDropPoint(e, n, dd);
32858         var node = n.node;
32859         
32860         // auto node expand check
32861         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
32862             this.queueExpand(node);
32863         }else if(pt != "append"){
32864             this.cancelExpand();
32865         }
32866         
32867         // set the insert point style on the target node
32868         var returnCls = this.dropNotAllowed;
32869         if(this.isValidDropPoint(n, pt, dd, e, data)){
32870            if(pt){
32871                var el = n.ddel;
32872                var cls;
32873                if(pt == "above"){
32874                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
32875                    cls = "x-tree-drag-insert-above";
32876                }else if(pt == "below"){
32877                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
32878                    cls = "x-tree-drag-insert-below";
32879                }else{
32880                    returnCls = "x-tree-drop-ok-append";
32881                    cls = "x-tree-drag-append";
32882                }
32883                if(this.lastInsertClass != cls){
32884                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
32885                    this.lastInsertClass = cls;
32886                }
32887            }
32888        }
32889        return returnCls;
32890     },
32891     
32892     onNodeOut : function(n, dd, e, data){
32893         this.cancelExpand();
32894         this.removeDropIndicators(n);
32895     },
32896     
32897     onNodeDrop : function(n, dd, e, data){
32898         var point = this.getDropPoint(e, n, dd);
32899         var targetNode = n.node;
32900         targetNode.ui.startDrop();
32901         if(!this.isValidDropPoint(n, point, dd, e, data)){
32902             targetNode.ui.endDrop();
32903             return false;
32904         }
32905         // first try to find the drop node
32906         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
32907         var dropEvent = {
32908             tree : this.tree,
32909             target: targetNode,
32910             data: data,
32911             point: point,
32912             source: dd,
32913             rawEvent: e,
32914             dropNode: dropNode,
32915             cancel: !dropNode   
32916         };
32917         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
32918         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
32919             targetNode.ui.endDrop();
32920             return false;
32921         }
32922         // allow target changing
32923         targetNode = dropEvent.target;
32924         if(point == "append" && !targetNode.isExpanded()){
32925             targetNode.expand(false, null, function(){
32926                 this.completeDrop(dropEvent);
32927             }.createDelegate(this));
32928         }else{
32929             this.completeDrop(dropEvent);
32930         }
32931         return true;
32932     },
32933     
32934     completeDrop : function(de){
32935         var ns = de.dropNode, p = de.point, t = de.target;
32936         if(!(ns instanceof Array)){
32937             ns = [ns];
32938         }
32939         var n;
32940         for(var i = 0, len = ns.length; i < len; i++){
32941             n = ns[i];
32942             if(p == "above"){
32943                 t.parentNode.insertBefore(n, t);
32944             }else if(p == "below"){
32945                 t.parentNode.insertBefore(n, t.nextSibling);
32946             }else{
32947                 t.appendChild(n);
32948             }
32949         }
32950         n.ui.focus();
32951         if(this.tree.hlDrop){
32952             n.ui.highlight();
32953         }
32954         t.ui.endDrop();
32955         this.tree.fireEvent("nodedrop", de);
32956     },
32957     
32958     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
32959         if(this.tree.hlDrop){
32960             dropNode.ui.focus();
32961             dropNode.ui.highlight();
32962         }
32963         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
32964     },
32965     
32966     getTree : function(){
32967         return this.tree;
32968     },
32969     
32970     removeDropIndicators : function(n){
32971         if(n && n.ddel){
32972             var el = n.ddel;
32973             Roo.fly(el).removeClass([
32974                     "x-tree-drag-insert-above",
32975                     "x-tree-drag-insert-below",
32976                     "x-tree-drag-append"]);
32977             this.lastInsertClass = "_noclass";
32978         }
32979     },
32980     
32981     beforeDragDrop : function(target, e, id){
32982         this.cancelExpand();
32983         return true;
32984     },
32985     
32986     afterRepair : function(data){
32987         if(data && Roo.enableFx){
32988             data.node.ui.highlight();
32989         }
32990         this.hideProxy();
32991     }    
32992 });
32993
32994 }/*
32995  * Based on:
32996  * Ext JS Library 1.1.1
32997  * Copyright(c) 2006-2007, Ext JS, LLC.
32998  *
32999  * Originally Released Under LGPL - original licence link has changed is not relivant.
33000  *
33001  * Fork - LGPL
33002  * <script type="text/javascript">
33003  */
33004  
33005
33006 if(Roo.dd.DragZone){
33007 Roo.tree.TreeDragZone = function(tree, config){
33008     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33009     this.tree = tree;
33010 };
33011
33012 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33013     ddGroup : "TreeDD",
33014     
33015     onBeforeDrag : function(data, e){
33016         var n = data.node;
33017         return n && n.draggable && !n.disabled;
33018     },
33019     
33020     onInitDrag : function(e){
33021         var data = this.dragData;
33022         this.tree.getSelectionModel().select(data.node);
33023         this.proxy.update("");
33024         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33025         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33026     },
33027     
33028     getRepairXY : function(e, data){
33029         return data.node.ui.getDDRepairXY();
33030     },
33031     
33032     onEndDrag : function(data, e){
33033         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33034     },
33035     
33036     onValidDrop : function(dd, e, id){
33037         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33038         this.hideProxy();
33039     },
33040     
33041     beforeInvalidDrop : function(e, id){
33042         // this scrolls the original position back into view
33043         var sm = this.tree.getSelectionModel();
33044         sm.clearSelections();
33045         sm.select(this.dragData.node);
33046     }
33047 });
33048 }/*
33049  * Based on:
33050  * Ext JS Library 1.1.1
33051  * Copyright(c) 2006-2007, Ext JS, LLC.
33052  *
33053  * Originally Released Under LGPL - original licence link has changed is not relivant.
33054  *
33055  * Fork - LGPL
33056  * <script type="text/javascript">
33057  */
33058 /**
33059  * @class Roo.tree.TreeEditor
33060  * @extends Roo.Editor
33061  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33062  * as the editor field.
33063  * @constructor
33064  * @param {TreePanel} tree
33065  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33066  */
33067 Roo.tree.TreeEditor = function(tree, config){
33068     config = config || {};
33069     var field = config.events ? config : new Roo.form.TextField(config);
33070     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33071
33072     this.tree = tree;
33073
33074     tree.on('beforeclick', this.beforeNodeClick, this);
33075     tree.getTreeEl().on('mousedown', this.hide, this);
33076     this.on('complete', this.updateNode, this);
33077     this.on('beforestartedit', this.fitToTree, this);
33078     this.on('startedit', this.bindScroll, this, {delay:10});
33079     this.on('specialkey', this.onSpecialKey, this);
33080 };
33081
33082 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33083     /**
33084      * @cfg {String} alignment
33085      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33086      */
33087     alignment: "l-l",
33088     // inherit
33089     autoSize: false,
33090     /**
33091      * @cfg {Boolean} hideEl
33092      * True to hide the bound element while the editor is displayed (defaults to false)
33093      */
33094     hideEl : false,
33095     /**
33096      * @cfg {String} cls
33097      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33098      */
33099     cls: "x-small-editor x-tree-editor",
33100     /**
33101      * @cfg {Boolean} shim
33102      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33103      */
33104     shim:false,
33105     // inherit
33106     shadow:"frame",
33107     /**
33108      * @cfg {Number} maxWidth
33109      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33110      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33111      * scroll and client offsets into account prior to each edit.
33112      */
33113     maxWidth: 250,
33114
33115     editDelay : 350,
33116
33117     // private
33118     fitToTree : function(ed, el){
33119         var td = this.tree.getTreeEl().dom, nd = el.dom;
33120         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33121             td.scrollLeft = nd.offsetLeft;
33122         }
33123         var w = Math.min(
33124                 this.maxWidth,
33125                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33126         this.setSize(w, '');
33127     },
33128
33129     // private
33130     triggerEdit : function(node){
33131         this.completeEdit();
33132         this.editNode = node;
33133         this.startEdit(node.ui.textNode, node.text);
33134     },
33135
33136     // private
33137     bindScroll : function(){
33138         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33139     },
33140
33141     // private
33142     beforeNodeClick : function(node, e){
33143         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33144         this.lastClick = new Date();
33145         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33146             e.stopEvent();
33147             this.triggerEdit(node);
33148             return false;
33149         }
33150     },
33151
33152     // private
33153     updateNode : function(ed, value){
33154         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33155         this.editNode.setText(value);
33156     },
33157
33158     // private
33159     onHide : function(){
33160         Roo.tree.TreeEditor.superclass.onHide.call(this);
33161         if(this.editNode){
33162             this.editNode.ui.focus();
33163         }
33164     },
33165
33166     // private
33167     onSpecialKey : function(field, e){
33168         var k = e.getKey();
33169         if(k == e.ESC){
33170             e.stopEvent();
33171             this.cancelEdit();
33172         }else if(k == e.ENTER && !e.hasModifier()){
33173             e.stopEvent();
33174             this.completeEdit();
33175         }
33176     }
33177 });//<Script type="text/javascript">
33178 /*
33179  * Based on:
33180  * Ext JS Library 1.1.1
33181  * Copyright(c) 2006-2007, Ext JS, LLC.
33182  *
33183  * Originally Released Under LGPL - original licence link has changed is not relivant.
33184  *
33185  * Fork - LGPL
33186  * <script type="text/javascript">
33187  */
33188  
33189 /**
33190  * Not documented??? - probably should be...
33191  */
33192
33193 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33194     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33195     
33196     renderElements : function(n, a, targetNode, bulkRender){
33197         //consel.log("renderElements?");
33198         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33199
33200         var t = n.getOwnerTree();
33201         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33202         
33203         var cols = t.columns;
33204         var bw = t.borderWidth;
33205         var c = cols[0];
33206         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33207          var cb = typeof a.checked == "boolean";
33208         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33209         var colcls = 'x-t-' + tid + '-c0';
33210         var buf = [
33211             '<li class="x-tree-node">',
33212             
33213                 
33214                 '<div class="x-tree-node-el ', a.cls,'">',
33215                     // extran...
33216                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33217                 
33218                 
33219                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33220                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33221                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33222                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33223                            (a.iconCls ? ' '+a.iconCls : ''),
33224                            '" unselectable="on" />',
33225                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33226                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33227                              
33228                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33229                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33230                             '<span unselectable="on" qtip="' + tx + '">',
33231                              tx,
33232                              '</span></a>' ,
33233                     '</div>',
33234                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33235                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33236                  ];
33237         
33238         for(var i = 1, len = cols.length; i < len; i++){
33239             c = cols[i];
33240             colcls = 'x-t-' + tid + '-c' +i;
33241             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33242             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33243                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33244                       "</div>");
33245          }
33246          
33247          buf.push(
33248             '</a>',
33249             '<div class="x-clear"></div></div>',
33250             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33251             "</li>");
33252         
33253         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33254             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33255                                 n.nextSibling.ui.getEl(), buf.join(""));
33256         }else{
33257             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33258         }
33259         var el = this.wrap.firstChild;
33260         this.elRow = el;
33261         this.elNode = el.firstChild;
33262         this.ranchor = el.childNodes[1];
33263         this.ctNode = this.wrap.childNodes[1];
33264         var cs = el.firstChild.childNodes;
33265         this.indentNode = cs[0];
33266         this.ecNode = cs[1];
33267         this.iconNode = cs[2];
33268         var index = 3;
33269         if(cb){
33270             this.checkbox = cs[3];
33271             index++;
33272         }
33273         this.anchor = cs[index];
33274         
33275         this.textNode = cs[index].firstChild;
33276         
33277         //el.on("click", this.onClick, this);
33278         //el.on("dblclick", this.onDblClick, this);
33279         
33280         
33281        // console.log(this);
33282     },
33283     initEvents : function(){
33284         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33285         
33286             
33287         var a = this.ranchor;
33288
33289         var el = Roo.get(a);
33290
33291         if(Roo.isOpera){ // opera render bug ignores the CSS
33292             el.setStyle("text-decoration", "none");
33293         }
33294
33295         el.on("click", this.onClick, this);
33296         el.on("dblclick", this.onDblClick, this);
33297         el.on("contextmenu", this.onContextMenu, this);
33298         
33299     },
33300     
33301     /*onSelectedChange : function(state){
33302         if(state){
33303             this.focus();
33304             this.addClass("x-tree-selected");
33305         }else{
33306             //this.blur();
33307             this.removeClass("x-tree-selected");
33308         }
33309     },*/
33310     addClass : function(cls){
33311         if(this.elRow){
33312             Roo.fly(this.elRow).addClass(cls);
33313         }
33314         
33315     },
33316     
33317     
33318     removeClass : function(cls){
33319         if(this.elRow){
33320             Roo.fly(this.elRow).removeClass(cls);
33321         }
33322     }
33323
33324     
33325     
33326 });//<Script type="text/javascript">
33327
33328 /*
33329  * Based on:
33330  * Ext JS Library 1.1.1
33331  * Copyright(c) 2006-2007, Ext JS, LLC.
33332  *
33333  * Originally Released Under LGPL - original licence link has changed is not relivant.
33334  *
33335  * Fork - LGPL
33336  * <script type="text/javascript">
33337  */
33338  
33339
33340 /**
33341  * @class Roo.tree.ColumnTree
33342  * @extends Roo.data.TreePanel
33343  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33344  * @cfg {int} borderWidth  compined right/left border allowance
33345  * @constructor
33346  * @param {String/HTMLElement/Element} el The container element
33347  * @param {Object} config
33348  */
33349 Roo.tree.ColumnTree =  function(el, config)
33350 {
33351    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33352    this.addEvents({
33353         /**
33354         * @event resize
33355         * Fire this event on a container when it resizes
33356         * @param {int} w Width
33357         * @param {int} h Height
33358         */
33359        "resize" : true
33360     });
33361     this.on('resize', this.onResize, this);
33362 };
33363
33364 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33365     //lines:false,
33366     
33367     
33368     borderWidth: Roo.isBorderBox ? 0 : 2, 
33369     headEls : false,
33370     
33371     render : function(){
33372         // add the header.....
33373        
33374         Roo.tree.ColumnTree.superclass.render.apply(this);
33375         
33376         this.el.addClass('x-column-tree');
33377         
33378         this.headers = this.el.createChild(
33379             {cls:'x-tree-headers'},this.innerCt.dom);
33380    
33381         var cols = this.columns, c;
33382         var totalWidth = 0;
33383         this.headEls = [];
33384         var  len = cols.length;
33385         for(var i = 0; i < len; i++){
33386              c = cols[i];
33387              totalWidth += c.width;
33388             this.headEls.push(this.headers.createChild({
33389                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33390                  cn: {
33391                      cls:'x-tree-hd-text',
33392                      html: c.header
33393                  },
33394                  style:'width:'+(c.width-this.borderWidth)+'px;'
33395              }));
33396         }
33397         this.headers.createChild({cls:'x-clear'});
33398         // prevent floats from wrapping when clipped
33399         this.headers.setWidth(totalWidth);
33400         //this.innerCt.setWidth(totalWidth);
33401         this.innerCt.setStyle({ overflow: 'auto' });
33402         this.onResize(this.width, this.height);
33403              
33404         
33405     },
33406     onResize : function(w,h)
33407     {
33408         this.height = h;
33409         this.width = w;
33410         // resize cols..
33411         this.innerCt.setWidth(this.width);
33412         this.innerCt.setHeight(this.height-20);
33413         
33414         // headers...
33415         var cols = this.columns, c;
33416         var totalWidth = 0;
33417         var expEl = false;
33418         var len = cols.length;
33419         for(var i = 0; i < len; i++){
33420             c = cols[i];
33421             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33422                 // it's the expander..
33423                 expEl  = this.headEls[i];
33424                 continue;
33425             }
33426             totalWidth += c.width;
33427             
33428         }
33429         if (expEl) {
33430             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33431         }
33432         this.headers.setWidth(w-20);
33433
33434         
33435         
33436         
33437     }
33438 });
33439 /*
33440  * Based on:
33441  * Ext JS Library 1.1.1
33442  * Copyright(c) 2006-2007, Ext JS, LLC.
33443  *
33444  * Originally Released Under LGPL - original licence link has changed is not relivant.
33445  *
33446  * Fork - LGPL
33447  * <script type="text/javascript">
33448  */
33449  
33450 /**
33451  * @class Roo.menu.Menu
33452  * @extends Roo.util.Observable
33453  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33454  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33455  * @constructor
33456  * Creates a new Menu
33457  * @param {Object} config Configuration options
33458  */
33459 Roo.menu.Menu = function(config){
33460     Roo.apply(this, config);
33461     this.id = this.id || Roo.id();
33462     this.addEvents({
33463         /**
33464          * @event beforeshow
33465          * Fires before this menu is displayed
33466          * @param {Roo.menu.Menu} this
33467          */
33468         beforeshow : true,
33469         /**
33470          * @event beforehide
33471          * Fires before this menu is hidden
33472          * @param {Roo.menu.Menu} this
33473          */
33474         beforehide : true,
33475         /**
33476          * @event show
33477          * Fires after this menu is displayed
33478          * @param {Roo.menu.Menu} this
33479          */
33480         show : true,
33481         /**
33482          * @event hide
33483          * Fires after this menu is hidden
33484          * @param {Roo.menu.Menu} this
33485          */
33486         hide : true,
33487         /**
33488          * @event click
33489          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33490          * @param {Roo.menu.Menu} this
33491          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33492          * @param {Roo.EventObject} e
33493          */
33494         click : true,
33495         /**
33496          * @event mouseover
33497          * Fires when the mouse is hovering over this menu
33498          * @param {Roo.menu.Menu} this
33499          * @param {Roo.EventObject} e
33500          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33501          */
33502         mouseover : true,
33503         /**
33504          * @event mouseout
33505          * Fires when the mouse exits this menu
33506          * @param {Roo.menu.Menu} this
33507          * @param {Roo.EventObject} e
33508          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33509          */
33510         mouseout : true,
33511         /**
33512          * @event itemclick
33513          * Fires when a menu item contained in this menu is clicked
33514          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33515          * @param {Roo.EventObject} e
33516          */
33517         itemclick: true
33518     });
33519     if (this.registerMenu) {
33520         Roo.menu.MenuMgr.register(this);
33521     }
33522     
33523     var mis = this.items;
33524     this.items = new Roo.util.MixedCollection();
33525     if(mis){
33526         this.add.apply(this, mis);
33527     }
33528 };
33529
33530 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33531     /**
33532      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33533      */
33534     minWidth : 120,
33535     /**
33536      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33537      * for bottom-right shadow (defaults to "sides")
33538      */
33539     shadow : "sides",
33540     /**
33541      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33542      * this menu (defaults to "tl-tr?")
33543      */
33544     subMenuAlign : "tl-tr?",
33545     /**
33546      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33547      * relative to its element of origin (defaults to "tl-bl?")
33548      */
33549     defaultAlign : "tl-bl?",
33550     /**
33551      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33552      */
33553     allowOtherMenus : false,
33554     /**
33555      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33556      */
33557     registerMenu : true,
33558
33559     hidden:true,
33560
33561     // private
33562     render : function(){
33563         if(this.el){
33564             return;
33565         }
33566         var el = this.el = new Roo.Layer({
33567             cls: "x-menu",
33568             shadow:this.shadow,
33569             constrain: false,
33570             parentEl: this.parentEl || document.body,
33571             zindex:15000
33572         });
33573
33574         this.keyNav = new Roo.menu.MenuNav(this);
33575
33576         if(this.plain){
33577             el.addClass("x-menu-plain");
33578         }
33579         if(this.cls){
33580             el.addClass(this.cls);
33581         }
33582         // generic focus element
33583         this.focusEl = el.createChild({
33584             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33585         });
33586         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33587         ul.on("click", this.onClick, this);
33588         ul.on("mouseover", this.onMouseOver, this);
33589         ul.on("mouseout", this.onMouseOut, this);
33590         this.items.each(function(item){
33591             var li = document.createElement("li");
33592             li.className = "x-menu-list-item";
33593             ul.dom.appendChild(li);
33594             item.render(li, this);
33595         }, this);
33596         this.ul = ul;
33597         this.autoWidth();
33598     },
33599
33600     // private
33601     autoWidth : function(){
33602         var el = this.el, ul = this.ul;
33603         if(!el){
33604             return;
33605         }
33606         var w = this.width;
33607         if(w){
33608             el.setWidth(w);
33609         }else if(Roo.isIE){
33610             el.setWidth(this.minWidth);
33611             var t = el.dom.offsetWidth; // force recalc
33612             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33613         }
33614     },
33615
33616     // private
33617     delayAutoWidth : function(){
33618         if(this.rendered){
33619             if(!this.awTask){
33620                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33621             }
33622             this.awTask.delay(20);
33623         }
33624     },
33625
33626     // private
33627     findTargetItem : function(e){
33628         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33629         if(t && t.menuItemId){
33630             return this.items.get(t.menuItemId);
33631         }
33632     },
33633
33634     // private
33635     onClick : function(e){
33636         var t;
33637         if(t = this.findTargetItem(e)){
33638             t.onClick(e);
33639             this.fireEvent("click", this, t, e);
33640         }
33641     },
33642
33643     // private
33644     setActiveItem : function(item, autoExpand){
33645         if(item != this.activeItem){
33646             if(this.activeItem){
33647                 this.activeItem.deactivate();
33648             }
33649             this.activeItem = item;
33650             item.activate(autoExpand);
33651         }else if(autoExpand){
33652             item.expandMenu();
33653         }
33654     },
33655
33656     // private
33657     tryActivate : function(start, step){
33658         var items = this.items;
33659         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33660             var item = items.get(i);
33661             if(!item.disabled && item.canActivate){
33662                 this.setActiveItem(item, false);
33663                 return item;
33664             }
33665         }
33666         return false;
33667     },
33668
33669     // private
33670     onMouseOver : function(e){
33671         var t;
33672         if(t = this.findTargetItem(e)){
33673             if(t.canActivate && !t.disabled){
33674                 this.setActiveItem(t, true);
33675             }
33676         }
33677         this.fireEvent("mouseover", this, e, t);
33678     },
33679
33680     // private
33681     onMouseOut : function(e){
33682         var t;
33683         if(t = this.findTargetItem(e)){
33684             if(t == this.activeItem && t.shouldDeactivate(e)){
33685                 this.activeItem.deactivate();
33686                 delete this.activeItem;
33687             }
33688         }
33689         this.fireEvent("mouseout", this, e, t);
33690     },
33691
33692     /**
33693      * Read-only.  Returns true if the menu is currently displayed, else false.
33694      * @type Boolean
33695      */
33696     isVisible : function(){
33697         return this.el && !this.hidden;
33698     },
33699
33700     /**
33701      * Displays this menu relative to another element
33702      * @param {String/HTMLElement/Roo.Element} element The element to align to
33703      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33704      * the element (defaults to this.defaultAlign)
33705      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33706      */
33707     show : function(el, pos, parentMenu){
33708         this.parentMenu = parentMenu;
33709         if(!this.el){
33710             this.render();
33711         }
33712         this.fireEvent("beforeshow", this);
33713         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33714     },
33715
33716     /**
33717      * Displays this menu at a specific xy position
33718      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33719      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33720      */
33721     showAt : function(xy, parentMenu, /* private: */_e){
33722         this.parentMenu = parentMenu;
33723         if(!this.el){
33724             this.render();
33725         }
33726         if(_e !== false){
33727             this.fireEvent("beforeshow", this);
33728             xy = this.el.adjustForConstraints(xy);
33729         }
33730         this.el.setXY(xy);
33731         this.el.show();
33732         this.hidden = false;
33733         this.focus();
33734         this.fireEvent("show", this);
33735     },
33736
33737     focus : function(){
33738         if(!this.hidden){
33739             this.doFocus.defer(50, this);
33740         }
33741     },
33742
33743     doFocus : function(){
33744         if(!this.hidden){
33745             this.focusEl.focus();
33746         }
33747     },
33748
33749     /**
33750      * Hides this menu and optionally all parent menus
33751      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33752      */
33753     hide : function(deep){
33754         if(this.el && this.isVisible()){
33755             this.fireEvent("beforehide", this);
33756             if(this.activeItem){
33757                 this.activeItem.deactivate();
33758                 this.activeItem = null;
33759             }
33760             this.el.hide();
33761             this.hidden = true;
33762             this.fireEvent("hide", this);
33763         }
33764         if(deep === true && this.parentMenu){
33765             this.parentMenu.hide(true);
33766         }
33767     },
33768
33769     /**
33770      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33771      * Any of the following are valid:
33772      * <ul>
33773      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33774      * <li>An HTMLElement object which will be converted to a menu item</li>
33775      * <li>A menu item config object that will be created as a new menu item</li>
33776      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33777      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33778      * </ul>
33779      * Usage:
33780      * <pre><code>
33781 // Create the menu
33782 var menu = new Roo.menu.Menu();
33783
33784 // Create a menu item to add by reference
33785 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33786
33787 // Add a bunch of items at once using different methods.
33788 // Only the last item added will be returned.
33789 var item = menu.add(
33790     menuItem,                // add existing item by ref
33791     'Dynamic Item',          // new TextItem
33792     '-',                     // new separator
33793     { text: 'Config Item' }  // new item by config
33794 );
33795 </code></pre>
33796      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33797      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33798      */
33799     add : function(){
33800         var a = arguments, l = a.length, item;
33801         for(var i = 0; i < l; i++){
33802             var el = a[i];
33803             if(el.render){ // some kind of Item
33804                 item = this.addItem(el);
33805             }else if(typeof el == "string"){ // string
33806                 if(el == "separator" || el == "-"){
33807                     item = this.addSeparator();
33808                 }else{
33809                     item = this.addText(el);
33810                 }
33811             }else if(el.tagName || el.el){ // element
33812                 item = this.addElement(el);
33813             }else if(typeof el == "object"){ // must be menu item config?
33814                 item = this.addMenuItem(el);
33815             }
33816         }
33817         return item;
33818     },
33819
33820     /**
33821      * Returns this menu's underlying {@link Roo.Element} object
33822      * @return {Roo.Element} The element
33823      */
33824     getEl : function(){
33825         if(!this.el){
33826             this.render();
33827         }
33828         return this.el;
33829     },
33830
33831     /**
33832      * Adds a separator bar to the menu
33833      * @return {Roo.menu.Item} The menu item that was added
33834      */
33835     addSeparator : function(){
33836         return this.addItem(new Roo.menu.Separator());
33837     },
33838
33839     /**
33840      * Adds an {@link Roo.Element} object to the menu
33841      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
33842      * @return {Roo.menu.Item} The menu item that was added
33843      */
33844     addElement : function(el){
33845         return this.addItem(new Roo.menu.BaseItem(el));
33846     },
33847
33848     /**
33849      * Adds an existing object based on {@link Roo.menu.Item} to the menu
33850      * @param {Roo.menu.Item} item The menu item to add
33851      * @return {Roo.menu.Item} The menu item that was added
33852      */
33853     addItem : function(item){
33854         this.items.add(item);
33855         if(this.ul){
33856             var li = document.createElement("li");
33857             li.className = "x-menu-list-item";
33858             this.ul.dom.appendChild(li);
33859             item.render(li, this);
33860             this.delayAutoWidth();
33861         }
33862         return item;
33863     },
33864
33865     /**
33866      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
33867      * @param {Object} config A MenuItem config object
33868      * @return {Roo.menu.Item} The menu item that was added
33869      */
33870     addMenuItem : function(config){
33871         if(!(config instanceof Roo.menu.Item)){
33872             if(typeof config.checked == "boolean"){ // must be check menu item config?
33873                 config = new Roo.menu.CheckItem(config);
33874             }else{
33875                 config = new Roo.menu.Item(config);
33876             }
33877         }
33878         return this.addItem(config);
33879     },
33880
33881     /**
33882      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
33883      * @param {String} text The text to display in the menu item
33884      * @return {Roo.menu.Item} The menu item that was added
33885      */
33886     addText : function(text){
33887         return this.addItem(new Roo.menu.TextItem(text));
33888     },
33889
33890     /**
33891      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
33892      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
33893      * @param {Roo.menu.Item} item The menu item to add
33894      * @return {Roo.menu.Item} The menu item that was added
33895      */
33896     insert : function(index, item){
33897         this.items.insert(index, item);
33898         if(this.ul){
33899             var li = document.createElement("li");
33900             li.className = "x-menu-list-item";
33901             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
33902             item.render(li, this);
33903             this.delayAutoWidth();
33904         }
33905         return item;
33906     },
33907
33908     /**
33909      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
33910      * @param {Roo.menu.Item} item The menu item to remove
33911      */
33912     remove : function(item){
33913         this.items.removeKey(item.id);
33914         item.destroy();
33915     },
33916
33917     /**
33918      * Removes and destroys all items in the menu
33919      */
33920     removeAll : function(){
33921         var f;
33922         while(f = this.items.first()){
33923             this.remove(f);
33924         }
33925     }
33926 });
33927
33928 // MenuNav is a private utility class used internally by the Menu
33929 Roo.menu.MenuNav = function(menu){
33930     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
33931     this.scope = this.menu = menu;
33932 };
33933
33934 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
33935     doRelay : function(e, h){
33936         var k = e.getKey();
33937         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
33938             this.menu.tryActivate(0, 1);
33939             return false;
33940         }
33941         return h.call(this.scope || this, e, this.menu);
33942     },
33943
33944     up : function(e, m){
33945         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
33946             m.tryActivate(m.items.length-1, -1);
33947         }
33948     },
33949
33950     down : function(e, m){
33951         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
33952             m.tryActivate(0, 1);
33953         }
33954     },
33955
33956     right : function(e, m){
33957         if(m.activeItem){
33958             m.activeItem.expandMenu(true);
33959         }
33960     },
33961
33962     left : function(e, m){
33963         m.hide();
33964         if(m.parentMenu && m.parentMenu.activeItem){
33965             m.parentMenu.activeItem.activate();
33966         }
33967     },
33968
33969     enter : function(e, m){
33970         if(m.activeItem){
33971             e.stopPropagation();
33972             m.activeItem.onClick(e);
33973             m.fireEvent("click", this, m.activeItem);
33974             return true;
33975         }
33976     }
33977 });/*
33978  * Based on:
33979  * Ext JS Library 1.1.1
33980  * Copyright(c) 2006-2007, Ext JS, LLC.
33981  *
33982  * Originally Released Under LGPL - original licence link has changed is not relivant.
33983  *
33984  * Fork - LGPL
33985  * <script type="text/javascript">
33986  */
33987  
33988 /**
33989  * @class Roo.menu.MenuMgr
33990  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
33991  * @singleton
33992  */
33993 Roo.menu.MenuMgr = function(){
33994    var menus, active, groups = {}, attached = false, lastShow = new Date();
33995
33996    // private - called when first menu is created
33997    function init(){
33998        menus = {};
33999        active = new Roo.util.MixedCollection();
34000        Roo.get(document).addKeyListener(27, function(){
34001            if(active.length > 0){
34002                hideAll();
34003            }
34004        });
34005    }
34006
34007    // private
34008    function hideAll(){
34009        if(active && active.length > 0){
34010            var c = active.clone();
34011            c.each(function(m){
34012                m.hide();
34013            });
34014        }
34015    }
34016
34017    // private
34018    function onHide(m){
34019        active.remove(m);
34020        if(active.length < 1){
34021            Roo.get(document).un("mousedown", onMouseDown);
34022            attached = false;
34023        }
34024    }
34025
34026    // private
34027    function onShow(m){
34028        var last = active.last();
34029        lastShow = new Date();
34030        active.add(m);
34031        if(!attached){
34032            Roo.get(document).on("mousedown", onMouseDown);
34033            attached = true;
34034        }
34035        if(m.parentMenu){
34036           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34037           m.parentMenu.activeChild = m;
34038        }else if(last && last.isVisible()){
34039           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34040        }
34041    }
34042
34043    // private
34044    function onBeforeHide(m){
34045        if(m.activeChild){
34046            m.activeChild.hide();
34047        }
34048        if(m.autoHideTimer){
34049            clearTimeout(m.autoHideTimer);
34050            delete m.autoHideTimer;
34051        }
34052    }
34053
34054    // private
34055    function onBeforeShow(m){
34056        var pm = m.parentMenu;
34057        if(!pm && !m.allowOtherMenus){
34058            hideAll();
34059        }else if(pm && pm.activeChild && active != m){
34060            pm.activeChild.hide();
34061        }
34062    }
34063
34064    // private
34065    function onMouseDown(e){
34066        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34067            hideAll();
34068        }
34069    }
34070
34071    // private
34072    function onBeforeCheck(mi, state){
34073        if(state){
34074            var g = groups[mi.group];
34075            for(var i = 0, l = g.length; i < l; i++){
34076                if(g[i] != mi){
34077                    g[i].setChecked(false);
34078                }
34079            }
34080        }
34081    }
34082
34083    return {
34084
34085        /**
34086         * Hides all menus that are currently visible
34087         */
34088        hideAll : function(){
34089             hideAll();  
34090        },
34091
34092        // private
34093        register : function(menu){
34094            if(!menus){
34095                init();
34096            }
34097            menus[menu.id] = menu;
34098            menu.on("beforehide", onBeforeHide);
34099            menu.on("hide", onHide);
34100            menu.on("beforeshow", onBeforeShow);
34101            menu.on("show", onShow);
34102            var g = menu.group;
34103            if(g && menu.events["checkchange"]){
34104                if(!groups[g]){
34105                    groups[g] = [];
34106                }
34107                groups[g].push(menu);
34108                menu.on("checkchange", onCheck);
34109            }
34110        },
34111
34112         /**
34113          * Returns a {@link Roo.menu.Menu} object
34114          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34115          * be used to generate and return a new Menu instance.
34116          */
34117        get : function(menu){
34118            if(typeof menu == "string"){ // menu id
34119                return menus[menu];
34120            }else if(menu.events){  // menu instance
34121                return menu;
34122            }else if(typeof menu.length == 'number'){ // array of menu items?
34123                return new Roo.menu.Menu({items:menu});
34124            }else{ // otherwise, must be a config
34125                return new Roo.menu.Menu(menu);
34126            }
34127        },
34128
34129        // private
34130        unregister : function(menu){
34131            delete menus[menu.id];
34132            menu.un("beforehide", onBeforeHide);
34133            menu.un("hide", onHide);
34134            menu.un("beforeshow", onBeforeShow);
34135            menu.un("show", onShow);
34136            var g = menu.group;
34137            if(g && menu.events["checkchange"]){
34138                groups[g].remove(menu);
34139                menu.un("checkchange", onCheck);
34140            }
34141        },
34142
34143        // private
34144        registerCheckable : function(menuItem){
34145            var g = menuItem.group;
34146            if(g){
34147                if(!groups[g]){
34148                    groups[g] = [];
34149                }
34150                groups[g].push(menuItem);
34151                menuItem.on("beforecheckchange", onBeforeCheck);
34152            }
34153        },
34154
34155        // private
34156        unregisterCheckable : function(menuItem){
34157            var g = menuItem.group;
34158            if(g){
34159                groups[g].remove(menuItem);
34160                menuItem.un("beforecheckchange", onBeforeCheck);
34161            }
34162        }
34163    };
34164 }();/*
34165  * Based on:
34166  * Ext JS Library 1.1.1
34167  * Copyright(c) 2006-2007, Ext JS, LLC.
34168  *
34169  * Originally Released Under LGPL - original licence link has changed is not relivant.
34170  *
34171  * Fork - LGPL
34172  * <script type="text/javascript">
34173  */
34174  
34175
34176 /**
34177  * @class Roo.menu.BaseItem
34178  * @extends Roo.Component
34179  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34180  * management and base configuration options shared by all menu components.
34181  * @constructor
34182  * Creates a new BaseItem
34183  * @param {Object} config Configuration options
34184  */
34185 Roo.menu.BaseItem = function(config){
34186     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34187
34188     this.addEvents({
34189         /**
34190          * @event click
34191          * Fires when this item is clicked
34192          * @param {Roo.menu.BaseItem} this
34193          * @param {Roo.EventObject} e
34194          */
34195         click: true,
34196         /**
34197          * @event activate
34198          * Fires when this item is activated
34199          * @param {Roo.menu.BaseItem} this
34200          */
34201         activate : true,
34202         /**
34203          * @event deactivate
34204          * Fires when this item is deactivated
34205          * @param {Roo.menu.BaseItem} this
34206          */
34207         deactivate : true
34208     });
34209
34210     if(this.handler){
34211         this.on("click", this.handler, this.scope, true);
34212     }
34213 };
34214
34215 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34216     /**
34217      * @cfg {Function} handler
34218      * A function that will handle the click event of this menu item (defaults to undefined)
34219      */
34220     /**
34221      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34222      */
34223     canActivate : false,
34224     /**
34225      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34226      */
34227     activeClass : "x-menu-item-active",
34228     /**
34229      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34230      */
34231     hideOnClick : true,
34232     /**
34233      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34234      */
34235     hideDelay : 100,
34236
34237     // private
34238     ctype: "Roo.menu.BaseItem",
34239
34240     // private
34241     actionMode : "container",
34242
34243     // private
34244     render : function(container, parentMenu){
34245         this.parentMenu = parentMenu;
34246         Roo.menu.BaseItem.superclass.render.call(this, container);
34247         this.container.menuItemId = this.id;
34248     },
34249
34250     // private
34251     onRender : function(container, position){
34252         this.el = Roo.get(this.el);
34253         container.dom.appendChild(this.el.dom);
34254     },
34255
34256     // private
34257     onClick : function(e){
34258         if(!this.disabled && this.fireEvent("click", this, e) !== false
34259                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34260             this.handleClick(e);
34261         }else{
34262             e.stopEvent();
34263         }
34264     },
34265
34266     // private
34267     activate : function(){
34268         if(this.disabled){
34269             return false;
34270         }
34271         var li = this.container;
34272         li.addClass(this.activeClass);
34273         this.region = li.getRegion().adjust(2, 2, -2, -2);
34274         this.fireEvent("activate", this);
34275         return true;
34276     },
34277
34278     // private
34279     deactivate : function(){
34280         this.container.removeClass(this.activeClass);
34281         this.fireEvent("deactivate", this);
34282     },
34283
34284     // private
34285     shouldDeactivate : function(e){
34286         return !this.region || !this.region.contains(e.getPoint());
34287     },
34288
34289     // private
34290     handleClick : function(e){
34291         if(this.hideOnClick){
34292             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34293         }
34294     },
34295
34296     // private
34297     expandMenu : function(autoActivate){
34298         // do nothing
34299     },
34300
34301     // private
34302     hideMenu : function(){
34303         // do nothing
34304     }
34305 });/*
34306  * Based on:
34307  * Ext JS Library 1.1.1
34308  * Copyright(c) 2006-2007, Ext JS, LLC.
34309  *
34310  * Originally Released Under LGPL - original licence link has changed is not relivant.
34311  *
34312  * Fork - LGPL
34313  * <script type="text/javascript">
34314  */
34315  
34316 /**
34317  * @class Roo.menu.Adapter
34318  * @extends Roo.menu.BaseItem
34319  * 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.
34320  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34321  * @constructor
34322  * Creates a new Adapter
34323  * @param {Object} config Configuration options
34324  */
34325 Roo.menu.Adapter = function(component, config){
34326     Roo.menu.Adapter.superclass.constructor.call(this, config);
34327     this.component = component;
34328 };
34329 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34330     // private
34331     canActivate : true,
34332
34333     // private
34334     onRender : function(container, position){
34335         this.component.render(container);
34336         this.el = this.component.getEl();
34337     },
34338
34339     // private
34340     activate : function(){
34341         if(this.disabled){
34342             return false;
34343         }
34344         this.component.focus();
34345         this.fireEvent("activate", this);
34346         return true;
34347     },
34348
34349     // private
34350     deactivate : function(){
34351         this.fireEvent("deactivate", this);
34352     },
34353
34354     // private
34355     disable : function(){
34356         this.component.disable();
34357         Roo.menu.Adapter.superclass.disable.call(this);
34358     },
34359
34360     // private
34361     enable : function(){
34362         this.component.enable();
34363         Roo.menu.Adapter.superclass.enable.call(this);
34364     }
34365 });/*
34366  * Based on:
34367  * Ext JS Library 1.1.1
34368  * Copyright(c) 2006-2007, Ext JS, LLC.
34369  *
34370  * Originally Released Under LGPL - original licence link has changed is not relivant.
34371  *
34372  * Fork - LGPL
34373  * <script type="text/javascript">
34374  */
34375
34376 /**
34377  * @class Roo.menu.TextItem
34378  * @extends Roo.menu.BaseItem
34379  * Adds a static text string to a menu, usually used as either a heading or group separator.
34380  * @constructor
34381  * Creates a new TextItem
34382  * @param {String} text The text to display
34383  */
34384 Roo.menu.TextItem = function(text){
34385     this.text = text;
34386     Roo.menu.TextItem.superclass.constructor.call(this);
34387 };
34388
34389 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34390     /**
34391      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34392      */
34393     hideOnClick : false,
34394     /**
34395      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34396      */
34397     itemCls : "x-menu-text",
34398
34399     // private
34400     onRender : function(){
34401         var s = document.createElement("span");
34402         s.className = this.itemCls;
34403         s.innerHTML = this.text;
34404         this.el = s;
34405         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34406     }
34407 });/*
34408  * Based on:
34409  * Ext JS Library 1.1.1
34410  * Copyright(c) 2006-2007, Ext JS, LLC.
34411  *
34412  * Originally Released Under LGPL - original licence link has changed is not relivant.
34413  *
34414  * Fork - LGPL
34415  * <script type="text/javascript">
34416  */
34417
34418 /**
34419  * @class Roo.menu.Separator
34420  * @extends Roo.menu.BaseItem
34421  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34422  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34423  * @constructor
34424  * @param {Object} config Configuration options
34425  */
34426 Roo.menu.Separator = function(config){
34427     Roo.menu.Separator.superclass.constructor.call(this, config);
34428 };
34429
34430 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34431     /**
34432      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34433      */
34434     itemCls : "x-menu-sep",
34435     /**
34436      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34437      */
34438     hideOnClick : false,
34439
34440     // private
34441     onRender : function(li){
34442         var s = document.createElement("span");
34443         s.className = this.itemCls;
34444         s.innerHTML = "&#160;";
34445         this.el = s;
34446         li.addClass("x-menu-sep-li");
34447         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34448     }
34449 });/*
34450  * Based on:
34451  * Ext JS Library 1.1.1
34452  * Copyright(c) 2006-2007, Ext JS, LLC.
34453  *
34454  * Originally Released Under LGPL - original licence link has changed is not relivant.
34455  *
34456  * Fork - LGPL
34457  * <script type="text/javascript">
34458  */
34459 /**
34460  * @class Roo.menu.Item
34461  * @extends Roo.menu.BaseItem
34462  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34463  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34464  * activation and click handling.
34465  * @constructor
34466  * Creates a new Item
34467  * @param {Object} config Configuration options
34468  */
34469 Roo.menu.Item = function(config){
34470     Roo.menu.Item.superclass.constructor.call(this, config);
34471     if(this.menu){
34472         this.menu = Roo.menu.MenuMgr.get(this.menu);
34473     }
34474 };
34475 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34476     /**
34477      * @cfg {String} icon
34478      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34479      */
34480     /**
34481      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34482      */
34483     itemCls : "x-menu-item",
34484     /**
34485      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34486      */
34487     canActivate : true,
34488     /**
34489      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34490      */
34491     showDelay: 200,
34492     // doc'd in BaseItem
34493     hideDelay: 200,
34494
34495     // private
34496     ctype: "Roo.menu.Item",
34497     
34498     // private
34499     onRender : function(container, position){
34500         var el = document.createElement("a");
34501         el.hideFocus = true;
34502         el.unselectable = "on";
34503         el.href = this.href || "#";
34504         if(this.hrefTarget){
34505             el.target = this.hrefTarget;
34506         }
34507         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34508         el.innerHTML = String.format(
34509                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
34510                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
34511         this.el = el;
34512         Roo.menu.Item.superclass.onRender.call(this, container, position);
34513     },
34514
34515     /**
34516      * Sets the text to display in this menu item
34517      * @param {String} text The text to display
34518      */
34519     setText : function(text){
34520         this.text = text;
34521         if(this.rendered){
34522             this.el.update(String.format(
34523                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
34524                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34525             this.parentMenu.autoWidth();
34526         }
34527     },
34528
34529     // private
34530     handleClick : function(e){
34531         if(!this.href){ // if no link defined, stop the event automatically
34532             e.stopEvent();
34533         }
34534         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34535     },
34536
34537     // private
34538     activate : function(autoExpand){
34539         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34540             this.focus();
34541             if(autoExpand){
34542                 this.expandMenu();
34543             }
34544         }
34545         return true;
34546     },
34547
34548     // private
34549     shouldDeactivate : function(e){
34550         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34551             if(this.menu && this.menu.isVisible()){
34552                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34553             }
34554             return true;
34555         }
34556         return false;
34557     },
34558
34559     // private
34560     deactivate : function(){
34561         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34562         this.hideMenu();
34563     },
34564
34565     // private
34566     expandMenu : function(autoActivate){
34567         if(!this.disabled && this.menu){
34568             clearTimeout(this.hideTimer);
34569             delete this.hideTimer;
34570             if(!this.menu.isVisible() && !this.showTimer){
34571                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34572             }else if (this.menu.isVisible() && autoActivate){
34573                 this.menu.tryActivate(0, 1);
34574             }
34575         }
34576     },
34577
34578     // private
34579     deferExpand : function(autoActivate){
34580         delete this.showTimer;
34581         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34582         if(autoActivate){
34583             this.menu.tryActivate(0, 1);
34584         }
34585     },
34586
34587     // private
34588     hideMenu : function(){
34589         clearTimeout(this.showTimer);
34590         delete this.showTimer;
34591         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34592             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34593         }
34594     },
34595
34596     // private
34597     deferHide : function(){
34598         delete this.hideTimer;
34599         this.menu.hide();
34600     }
34601 });/*
34602  * Based on:
34603  * Ext JS Library 1.1.1
34604  * Copyright(c) 2006-2007, Ext JS, LLC.
34605  *
34606  * Originally Released Under LGPL - original licence link has changed is not relivant.
34607  *
34608  * Fork - LGPL
34609  * <script type="text/javascript">
34610  */
34611  
34612 /**
34613  * @class Roo.menu.CheckItem
34614  * @extends Roo.menu.Item
34615  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34616  * @constructor
34617  * Creates a new CheckItem
34618  * @param {Object} config Configuration options
34619  */
34620 Roo.menu.CheckItem = function(config){
34621     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34622     this.addEvents({
34623         /**
34624          * @event beforecheckchange
34625          * Fires before the checked value is set, providing an opportunity to cancel if needed
34626          * @param {Roo.menu.CheckItem} this
34627          * @param {Boolean} checked The new checked value that will be set
34628          */
34629         "beforecheckchange" : true,
34630         /**
34631          * @event checkchange
34632          * Fires after the checked value has been set
34633          * @param {Roo.menu.CheckItem} this
34634          * @param {Boolean} checked The checked value that was set
34635          */
34636         "checkchange" : true
34637     });
34638     if(this.checkHandler){
34639         this.on('checkchange', this.checkHandler, this.scope);
34640     }
34641 };
34642 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34643     /**
34644      * @cfg {String} group
34645      * All check items with the same group name will automatically be grouped into a single-select
34646      * radio button group (defaults to '')
34647      */
34648     /**
34649      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34650      */
34651     itemCls : "x-menu-item x-menu-check-item",
34652     /**
34653      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34654      */
34655     groupClass : "x-menu-group-item",
34656
34657     /**
34658      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34659      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34660      * initialized with checked = true will be rendered as checked.
34661      */
34662     checked: false,
34663
34664     // private
34665     ctype: "Roo.menu.CheckItem",
34666
34667     // private
34668     onRender : function(c){
34669         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34670         if(this.group){
34671             this.el.addClass(this.groupClass);
34672         }
34673         Roo.menu.MenuMgr.registerCheckable(this);
34674         if(this.checked){
34675             this.checked = false;
34676             this.setChecked(true, true);
34677         }
34678     },
34679
34680     // private
34681     destroy : function(){
34682         if(this.rendered){
34683             Roo.menu.MenuMgr.unregisterCheckable(this);
34684         }
34685         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34686     },
34687
34688     /**
34689      * Set the checked state of this item
34690      * @param {Boolean} checked The new checked value
34691      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34692      */
34693     setChecked : function(state, suppressEvent){
34694         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34695             if(this.container){
34696                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34697             }
34698             this.checked = state;
34699             if(suppressEvent !== true){
34700                 this.fireEvent("checkchange", this, state);
34701             }
34702         }
34703     },
34704
34705     // private
34706     handleClick : function(e){
34707        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34708            this.setChecked(!this.checked);
34709        }
34710        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34711     }
34712 });/*
34713  * Based on:
34714  * Ext JS Library 1.1.1
34715  * Copyright(c) 2006-2007, Ext JS, LLC.
34716  *
34717  * Originally Released Under LGPL - original licence link has changed is not relivant.
34718  *
34719  * Fork - LGPL
34720  * <script type="text/javascript">
34721  */
34722  
34723 /**
34724  * @class Roo.menu.DateItem
34725  * @extends Roo.menu.Adapter
34726  * A menu item that wraps the {@link Roo.DatPicker} component.
34727  * @constructor
34728  * Creates a new DateItem
34729  * @param {Object} config Configuration options
34730  */
34731 Roo.menu.DateItem = function(config){
34732     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34733     /** The Roo.DatePicker object @type Roo.DatePicker */
34734     this.picker = this.component;
34735     this.addEvents({select: true});
34736     
34737     this.picker.on("render", function(picker){
34738         picker.getEl().swallowEvent("click");
34739         picker.container.addClass("x-menu-date-item");
34740     });
34741
34742     this.picker.on("select", this.onSelect, this);
34743 };
34744
34745 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34746     // private
34747     onSelect : function(picker, date){
34748         this.fireEvent("select", this, date, picker);
34749         Roo.menu.DateItem.superclass.handleClick.call(this);
34750     }
34751 });/*
34752  * Based on:
34753  * Ext JS Library 1.1.1
34754  * Copyright(c) 2006-2007, Ext JS, LLC.
34755  *
34756  * Originally Released Under LGPL - original licence link has changed is not relivant.
34757  *
34758  * Fork - LGPL
34759  * <script type="text/javascript">
34760  */
34761  
34762 /**
34763  * @class Roo.menu.ColorItem
34764  * @extends Roo.menu.Adapter
34765  * A menu item that wraps the {@link Roo.ColorPalette} component.
34766  * @constructor
34767  * Creates a new ColorItem
34768  * @param {Object} config Configuration options
34769  */
34770 Roo.menu.ColorItem = function(config){
34771     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34772     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34773     this.palette = this.component;
34774     this.relayEvents(this.palette, ["select"]);
34775     if(this.selectHandler){
34776         this.on('select', this.selectHandler, this.scope);
34777     }
34778 };
34779 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34780  * Based on:
34781  * Ext JS Library 1.1.1
34782  * Copyright(c) 2006-2007, Ext JS, LLC.
34783  *
34784  * Originally Released Under LGPL - original licence link has changed is not relivant.
34785  *
34786  * Fork - LGPL
34787  * <script type="text/javascript">
34788  */
34789  
34790
34791 /**
34792  * @class Roo.menu.DateMenu
34793  * @extends Roo.menu.Menu
34794  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34795  * @constructor
34796  * Creates a new DateMenu
34797  * @param {Object} config Configuration options
34798  */
34799 Roo.menu.DateMenu = function(config){
34800     Roo.menu.DateMenu.superclass.constructor.call(this, config);
34801     this.plain = true;
34802     var di = new Roo.menu.DateItem(config);
34803     this.add(di);
34804     /**
34805      * The {@link Roo.DatePicker} instance for this DateMenu
34806      * @type DatePicker
34807      */
34808     this.picker = di.picker;
34809     /**
34810      * @event select
34811      * @param {DatePicker} picker
34812      * @param {Date} date
34813      */
34814     this.relayEvents(di, ["select"]);
34815
34816     this.on('beforeshow', function(){
34817         if(this.picker){
34818             this.picker.hideMonthPicker(true);
34819         }
34820     }, this);
34821 };
34822 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
34823     cls:'x-date-menu'
34824 });/*
34825  * Based on:
34826  * Ext JS Library 1.1.1
34827  * Copyright(c) 2006-2007, Ext JS, LLC.
34828  *
34829  * Originally Released Under LGPL - original licence link has changed is not relivant.
34830  *
34831  * Fork - LGPL
34832  * <script type="text/javascript">
34833  */
34834  
34835
34836 /**
34837  * @class Roo.menu.ColorMenu
34838  * @extends Roo.menu.Menu
34839  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
34840  * @constructor
34841  * Creates a new ColorMenu
34842  * @param {Object} config Configuration options
34843  */
34844 Roo.menu.ColorMenu = function(config){
34845     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
34846     this.plain = true;
34847     var ci = new Roo.menu.ColorItem(config);
34848     this.add(ci);
34849     /**
34850      * The {@link Roo.ColorPalette} instance for this ColorMenu
34851      * @type ColorPalette
34852      */
34853     this.palette = ci.palette;
34854     /**
34855      * @event select
34856      * @param {ColorPalette} palette
34857      * @param {String} color
34858      */
34859     this.relayEvents(ci, ["select"]);
34860 };
34861 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
34862  * Based on:
34863  * Ext JS Library 1.1.1
34864  * Copyright(c) 2006-2007, Ext JS, LLC.
34865  *
34866  * Originally Released Under LGPL - original licence link has changed is not relivant.
34867  *
34868  * Fork - LGPL
34869  * <script type="text/javascript">
34870  */
34871  
34872 /**
34873  * @class Roo.form.Field
34874  * @extends Roo.BoxComponent
34875  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
34876  * @constructor
34877  * Creates a new Field
34878  * @param {Object} config Configuration options
34879  */
34880 Roo.form.Field = function(config){
34881     Roo.form.Field.superclass.constructor.call(this, config);
34882 };
34883
34884 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
34885     /**
34886      * @cfg {String} fieldLabel Label to use when rendering a form.
34887      */
34888        /**
34889      * @cfg {String} qtip Mouse over tip
34890      */
34891      
34892     /**
34893      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
34894      */
34895     invalidClass : "x-form-invalid",
34896     /**
34897      * @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")
34898      */
34899     invalidText : "The value in this field is invalid",
34900     /**
34901      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
34902      */
34903     focusClass : "x-form-focus",
34904     /**
34905      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
34906       automatic validation (defaults to "keyup").
34907      */
34908     validationEvent : "keyup",
34909     /**
34910      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
34911      */
34912     validateOnBlur : true,
34913     /**
34914      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
34915      */
34916     validationDelay : 250,
34917     /**
34918      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
34919      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
34920      */
34921     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
34922     /**
34923      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
34924      */
34925     fieldClass : "x-form-field",
34926     /**
34927      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
34928      *<pre>
34929 Value         Description
34930 -----------   ----------------------------------------------------------------------
34931 qtip          Display a quick tip when the user hovers over the field
34932 title         Display a default browser title attribute popup
34933 under         Add a block div beneath the field containing the error text
34934 side          Add an error icon to the right of the field with a popup on hover
34935 [element id]  Add the error text directly to the innerHTML of the specified element
34936 </pre>
34937      */
34938     msgTarget : 'qtip',
34939     /**
34940      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
34941      */
34942     msgFx : 'normal',
34943
34944     /**
34945      * @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.
34946      */
34947     readOnly : false,
34948
34949     /**
34950      * @cfg {Boolean} disabled True to disable the field (defaults to false).
34951      */
34952     disabled : false,
34953
34954     /**
34955      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
34956      */
34957     inputType : undefined,
34958     
34959     /**
34960      * @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).
34961          */
34962         tabIndex : undefined,
34963         
34964     // private
34965     isFormField : true,
34966
34967     // private
34968     hasFocus : false,
34969     /**
34970      * @property {Roo.Element} fieldEl
34971      * Element Containing the rendered Field (with label etc.)
34972      */
34973     /**
34974      * @cfg {Mixed} value A value to initialize this field with.
34975      */
34976     value : undefined,
34977
34978     /**
34979      * @cfg {String} name The field's HTML name attribute.
34980      */
34981     /**
34982      * @cfg {String} cls A CSS class to apply to the field's underlying element.
34983      */
34984
34985         // private ??
34986         initComponent : function(){
34987         Roo.form.Field.superclass.initComponent.call(this);
34988         this.addEvents({
34989             /**
34990              * @event focus
34991              * Fires when this field receives input focus.
34992              * @param {Roo.form.Field} this
34993              */
34994             focus : true,
34995             /**
34996              * @event blur
34997              * Fires when this field loses input focus.
34998              * @param {Roo.form.Field} this
34999              */
35000             blur : true,
35001             /**
35002              * @event specialkey
35003              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35004              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35005              * @param {Roo.form.Field} this
35006              * @param {Roo.EventObject} e The event object
35007              */
35008             specialkey : true,
35009             /**
35010              * @event change
35011              * Fires just before the field blurs if the field value has changed.
35012              * @param {Roo.form.Field} this
35013              * @param {Mixed} newValue The new value
35014              * @param {Mixed} oldValue The original value
35015              */
35016             change : true,
35017             /**
35018              * @event invalid
35019              * Fires after the field has been marked as invalid.
35020              * @param {Roo.form.Field} this
35021              * @param {String} msg The validation message
35022              */
35023             invalid : true,
35024             /**
35025              * @event valid
35026              * Fires after the field has been validated with no errors.
35027              * @param {Roo.form.Field} this
35028              */
35029             valid : true
35030         });
35031     },
35032
35033     /**
35034      * Returns the name attribute of the field if available
35035      * @return {String} name The field name
35036      */
35037     getName: function(){
35038          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35039     },
35040
35041     // private
35042     onRender : function(ct, position){
35043         Roo.form.Field.superclass.onRender.call(this, ct, position);
35044         if(!this.el){
35045             var cfg = this.getAutoCreate();
35046             if(!cfg.name){
35047                 cfg.name = this.name || this.id;
35048             }
35049             if(this.inputType){
35050                 cfg.type = this.inputType;
35051             }
35052             this.el = ct.createChild(cfg, position);
35053         }
35054         var type = this.el.dom.type;
35055         if(type){
35056             if(type == 'password'){
35057                 type = 'text';
35058             }
35059             this.el.addClass('x-form-'+type);
35060         }
35061         if(this.readOnly){
35062             this.el.dom.readOnly = true;
35063         }
35064         if(this.tabIndex !== undefined){
35065             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35066         }
35067
35068         this.el.addClass([this.fieldClass, this.cls]);
35069         this.initValue();
35070     },
35071
35072     /**
35073      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35074      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35075      * @return {Roo.form.Field} this
35076      */
35077     applyTo : function(target){
35078         this.allowDomMove = false;
35079         this.el = Roo.get(target);
35080         this.render(this.el.dom.parentNode);
35081         return this;
35082     },
35083
35084     // private
35085     initValue : function(){
35086         if(this.value !== undefined){
35087             this.setValue(this.value);
35088         }else if(this.el.dom.value.length > 0){
35089             this.setValue(this.el.dom.value);
35090         }
35091     },
35092
35093     /**
35094      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35095      */
35096     isDirty : function() {
35097         if(this.disabled) {
35098             return false;
35099         }
35100         return String(this.getValue()) !== String(this.originalValue);
35101     },
35102
35103     // private
35104     afterRender : function(){
35105         Roo.form.Field.superclass.afterRender.call(this);
35106         this.initEvents();
35107     },
35108
35109     // private
35110     fireKey : function(e){
35111         if(e.isNavKeyPress()){
35112             this.fireEvent("specialkey", this, e);
35113         }
35114     },
35115
35116     /**
35117      * Resets the current field value to the originally loaded value and clears any validation messages
35118      */
35119     reset : function(){
35120         this.setValue(this.originalValue);
35121         this.clearInvalid();
35122     },
35123
35124     // private
35125     initEvents : function(){
35126         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
35127         this.el.on("focus", this.onFocus,  this);
35128         this.el.on("blur", this.onBlur,  this);
35129
35130         // reference to original value for reset
35131         this.originalValue = this.getValue();
35132     },
35133
35134     // private
35135     onFocus : function(){
35136         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35137             this.el.addClass(this.focusClass);
35138         }
35139         if(!this.hasFocus){
35140             this.hasFocus = true;
35141             this.startValue = this.getValue();
35142             this.fireEvent("focus", this);
35143         }
35144     },
35145
35146     beforeBlur : Roo.emptyFn,
35147
35148     // private
35149     onBlur : function(){
35150         this.beforeBlur();
35151         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35152             this.el.removeClass(this.focusClass);
35153         }
35154         this.hasFocus = false;
35155         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35156             this.validate();
35157         }
35158         var v = this.getValue();
35159         if(String(v) !== String(this.startValue)){
35160             this.fireEvent('change', this, v, this.startValue);
35161         }
35162         this.fireEvent("blur", this);
35163     },
35164
35165     /**
35166      * Returns whether or not the field value is currently valid
35167      * @param {Boolean} preventMark True to disable marking the field invalid
35168      * @return {Boolean} True if the value is valid, else false
35169      */
35170     isValid : function(preventMark){
35171         if(this.disabled){
35172             return true;
35173         }
35174         var restore = this.preventMark;
35175         this.preventMark = preventMark === true;
35176         var v = this.validateValue(this.processValue(this.getRawValue()));
35177         this.preventMark = restore;
35178         return v;
35179     },
35180
35181     /**
35182      * Validates the field value
35183      * @return {Boolean} True if the value is valid, else false
35184      */
35185     validate : function(){
35186         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35187             this.clearInvalid();
35188             return true;
35189         }
35190         return false;
35191     },
35192
35193     processValue : function(value){
35194         return value;
35195     },
35196
35197     // private
35198     // Subclasses should provide the validation implementation by overriding this
35199     validateValue : function(value){
35200         return true;
35201     },
35202
35203     /**
35204      * Mark this field as invalid
35205      * @param {String} msg The validation message
35206      */
35207     markInvalid : function(msg){
35208         if(!this.rendered || this.preventMark){ // not rendered
35209             return;
35210         }
35211         this.el.addClass(this.invalidClass);
35212         msg = msg || this.invalidText;
35213         switch(this.msgTarget){
35214             case 'qtip':
35215                 this.el.dom.qtip = msg;
35216                 this.el.dom.qclass = 'x-form-invalid-tip';
35217                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35218                     Roo.QuickTips.enable();
35219                 }
35220                 break;
35221             case 'title':
35222                 this.el.dom.title = msg;
35223                 break;
35224             case 'under':
35225                 if(!this.errorEl){
35226                     var elp = this.el.findParent('.x-form-element', 5, true);
35227                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35228                     this.errorEl.setWidth(elp.getWidth(true)-20);
35229                 }
35230                 this.errorEl.update(msg);
35231                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35232                 break;
35233             case 'side':
35234                 if(!this.errorIcon){
35235                     var elp = this.el.findParent('.x-form-element', 5, true);
35236                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35237                 }
35238                 this.alignErrorIcon();
35239                 this.errorIcon.dom.qtip = msg;
35240                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35241                 this.errorIcon.show();
35242                 this.on('resize', this.alignErrorIcon, this);
35243                 break;
35244             default:
35245                 var t = Roo.getDom(this.msgTarget);
35246                 t.innerHTML = msg;
35247                 t.style.display = this.msgDisplay;
35248                 break;
35249         }
35250         this.fireEvent('invalid', this, msg);
35251     },
35252
35253     // private
35254     alignErrorIcon : function(){
35255         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35256     },
35257
35258     /**
35259      * Clear any invalid styles/messages for this field
35260      */
35261     clearInvalid : function(){
35262         if(!this.rendered || this.preventMark){ // not rendered
35263             return;
35264         }
35265         this.el.removeClass(this.invalidClass);
35266         switch(this.msgTarget){
35267             case 'qtip':
35268                 this.el.dom.qtip = '';
35269                 break;
35270             case 'title':
35271                 this.el.dom.title = '';
35272                 break;
35273             case 'under':
35274                 if(this.errorEl){
35275                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35276                 }
35277                 break;
35278             case 'side':
35279                 if(this.errorIcon){
35280                     this.errorIcon.dom.qtip = '';
35281                     this.errorIcon.hide();
35282                     this.un('resize', this.alignErrorIcon, this);
35283                 }
35284                 break;
35285             default:
35286                 var t = Roo.getDom(this.msgTarget);
35287                 t.innerHTML = '';
35288                 t.style.display = 'none';
35289                 break;
35290         }
35291         this.fireEvent('valid', this);
35292     },
35293
35294     /**
35295      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35296      * @return {Mixed} value The field value
35297      */
35298     getRawValue : function(){
35299         var v = this.el.getValue();
35300         if(v === this.emptyText){
35301             v = '';
35302         }
35303         return v;
35304     },
35305
35306     /**
35307      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35308      * @return {Mixed} value The field value
35309      */
35310     getValue : function(){
35311         var v = this.el.getValue();
35312         if(v === this.emptyText || v === undefined){
35313             v = '';
35314         }
35315         return v;
35316     },
35317
35318     /**
35319      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35320      * @param {Mixed} value The value to set
35321      */
35322     setRawValue : function(v){
35323         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35324     },
35325
35326     /**
35327      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35328      * @param {Mixed} value The value to set
35329      */
35330     setValue : function(v){
35331         this.value = v;
35332         if(this.rendered){
35333             this.el.dom.value = (v === null || v === undefined ? '' : v);
35334             this.validate();
35335         }
35336     },
35337
35338     adjustSize : function(w, h){
35339         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35340         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35341         return s;
35342     },
35343
35344     adjustWidth : function(tag, w){
35345         tag = tag.toLowerCase();
35346         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35347             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35348                 if(tag == 'input'){
35349                     return w + 2;
35350                 }
35351                 if(tag = 'textarea'){
35352                     return w-2;
35353                 }
35354             }else if(Roo.isOpera){
35355                 if(tag == 'input'){
35356                     return w + 2;
35357                 }
35358                 if(tag = 'textarea'){
35359                     return w-2;
35360                 }
35361             }
35362         }
35363         return w;
35364     }
35365 });
35366
35367
35368 // anything other than normal should be considered experimental
35369 Roo.form.Field.msgFx = {
35370     normal : {
35371         show: function(msgEl, f){
35372             msgEl.setDisplayed('block');
35373         },
35374
35375         hide : function(msgEl, f){
35376             msgEl.setDisplayed(false).update('');
35377         }
35378     },
35379
35380     slide : {
35381         show: function(msgEl, f){
35382             msgEl.slideIn('t', {stopFx:true});
35383         },
35384
35385         hide : function(msgEl, f){
35386             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35387         }
35388     },
35389
35390     slideRight : {
35391         show: function(msgEl, f){
35392             msgEl.fixDisplay();
35393             msgEl.alignTo(f.el, 'tl-tr');
35394             msgEl.slideIn('l', {stopFx:true});
35395         },
35396
35397         hide : function(msgEl, f){
35398             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35399         }
35400     }
35401 };/*
35402  * Based on:
35403  * Ext JS Library 1.1.1
35404  * Copyright(c) 2006-2007, Ext JS, LLC.
35405  *
35406  * Originally Released Under LGPL - original licence link has changed is not relivant.
35407  *
35408  * Fork - LGPL
35409  * <script type="text/javascript">
35410  */
35411  
35412
35413 /**
35414  * @class Roo.form.TextField
35415  * @extends Roo.form.Field
35416  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35417  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35418  * @constructor
35419  * Creates a new TextField
35420  * @param {Object} config Configuration options
35421  */
35422 Roo.form.TextField = function(config){
35423     Roo.form.TextField.superclass.constructor.call(this, config);
35424     this.addEvents({
35425         /**
35426          * @event autosize
35427          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35428          * according to the default logic, but this event provides a hook for the developer to apply additional
35429          * logic at runtime to resize the field if needed.
35430              * @param {Roo.form.Field} this This text field
35431              * @param {Number} width The new field width
35432              */
35433         autosize : true
35434     });
35435 };
35436
35437 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35438     /**
35439      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35440      */
35441     grow : false,
35442     /**
35443      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35444      */
35445     growMin : 30,
35446     /**
35447      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35448      */
35449     growMax : 800,
35450     /**
35451      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35452      */
35453     vtype : null,
35454     /**
35455      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35456      */
35457     maskRe : null,
35458     /**
35459      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35460      */
35461     disableKeyFilter : false,
35462     /**
35463      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35464      */
35465     allowBlank : true,
35466     /**
35467      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35468      */
35469     minLength : 0,
35470     /**
35471      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35472      */
35473     maxLength : Number.MAX_VALUE,
35474     /**
35475      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35476      */
35477     minLengthText : "The minimum length for this field is {0}",
35478     /**
35479      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35480      */
35481     maxLengthText : "The maximum length for this field is {0}",
35482     /**
35483      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35484      */
35485     selectOnFocus : false,
35486     /**
35487      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35488      */
35489     blankText : "This field is required",
35490     /**
35491      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35492      * If available, this function will be called only after the basic validators all return true, and will be passed the
35493      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35494      */
35495     validator : null,
35496     /**
35497      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35498      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35499      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35500      */
35501     regex : null,
35502     /**
35503      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35504      */
35505     regexText : "",
35506     /**
35507      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35508      */
35509     emptyText : null,
35510     /**
35511      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35512      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35513      */
35514     emptyClass : 'x-form-empty-field',
35515
35516     // private
35517     initEvents : function(){
35518         Roo.form.TextField.superclass.initEvents.call(this);
35519         if(this.validationEvent == 'keyup'){
35520             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35521             this.el.on('keyup', this.filterValidation, this);
35522         }
35523         else if(this.validationEvent !== false){
35524             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35525         }
35526         if(this.selectOnFocus || this.emptyText){
35527             this.on("focus", this.preFocus, this);
35528             if(this.emptyText){
35529                 this.on('blur', this.postBlur, this);
35530                 this.applyEmptyText();
35531             }
35532         }
35533         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35534             this.el.on("keypress", this.filterKeys, this);
35535         }
35536         if(this.grow){
35537             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35538             this.el.on("click", this.autoSize,  this);
35539         }
35540     },
35541
35542     processValue : function(value){
35543         if(this.stripCharsRe){
35544             var newValue = value.replace(this.stripCharsRe, '');
35545             if(newValue !== value){
35546                 this.setRawValue(newValue);
35547                 return newValue;
35548             }
35549         }
35550         return value;
35551     },
35552
35553     filterValidation : function(e){
35554         if(!e.isNavKeyPress()){
35555             this.validationTask.delay(this.validationDelay);
35556         }
35557     },
35558
35559     // private
35560     onKeyUp : function(e){
35561         if(!e.isNavKeyPress()){
35562             this.autoSize();
35563         }
35564     },
35565
35566     /**
35567      * Resets the current field value to the originally-loaded value and clears any validation messages.
35568      * Also adds emptyText and emptyClass if the original value was blank.
35569      */
35570     reset : function(){
35571         Roo.form.TextField.superclass.reset.call(this);
35572         this.applyEmptyText();
35573     },
35574
35575     applyEmptyText : function(){
35576         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35577             this.setRawValue(this.emptyText);
35578             this.el.addClass(this.emptyClass);
35579         }
35580     },
35581
35582     // private
35583     preFocus : function(){
35584         if(this.emptyText){
35585             if(this.el.dom.value == this.emptyText){
35586                 this.setRawValue('');
35587             }
35588             this.el.removeClass(this.emptyClass);
35589         }
35590         if(this.selectOnFocus){
35591             this.el.dom.select();
35592         }
35593     },
35594
35595     // private
35596     postBlur : function(){
35597         this.applyEmptyText();
35598     },
35599
35600     // private
35601     filterKeys : function(e){
35602         var k = e.getKey();
35603         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35604             return;
35605         }
35606         var c = e.getCharCode(), cc = String.fromCharCode(c);
35607         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35608             return;
35609         }
35610         if(!this.maskRe.test(cc)){
35611             e.stopEvent();
35612         }
35613     },
35614
35615     setValue : function(v){
35616         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35617             this.el.removeClass(this.emptyClass);
35618         }
35619         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35620         this.applyEmptyText();
35621         this.autoSize();
35622     },
35623
35624     /**
35625      * Validates a value according to the field's validation rules and marks the field as invalid
35626      * if the validation fails
35627      * @param {Mixed} value The value to validate
35628      * @return {Boolean} True if the value is valid, else false
35629      */
35630     validateValue : function(value){
35631         if(value.length < 1 || value === this.emptyText){ // if it's blank
35632              if(this.allowBlank){
35633                 this.clearInvalid();
35634                 return true;
35635              }else{
35636                 this.markInvalid(this.blankText);
35637                 return false;
35638              }
35639         }
35640         if(value.length < this.minLength){
35641             this.markInvalid(String.format(this.minLengthText, this.minLength));
35642             return false;
35643         }
35644         if(value.length > this.maxLength){
35645             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35646             return false;
35647         }
35648         if(this.vtype){
35649             var vt = Roo.form.VTypes;
35650             if(!vt[this.vtype](value, this)){
35651                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35652                 return false;
35653             }
35654         }
35655         if(typeof this.validator == "function"){
35656             var msg = this.validator(value);
35657             if(msg !== true){
35658                 this.markInvalid(msg);
35659                 return false;
35660             }
35661         }
35662         if(this.regex && !this.regex.test(value)){
35663             this.markInvalid(this.regexText);
35664             return false;
35665         }
35666         return true;
35667     },
35668
35669     /**
35670      * Selects text in this field
35671      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35672      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35673      */
35674     selectText : function(start, end){
35675         var v = this.getRawValue();
35676         if(v.length > 0){
35677             start = start === undefined ? 0 : start;
35678             end = end === undefined ? v.length : end;
35679             var d = this.el.dom;
35680             if(d.setSelectionRange){
35681                 d.setSelectionRange(start, end);
35682             }else if(d.createTextRange){
35683                 var range = d.createTextRange();
35684                 range.moveStart("character", start);
35685                 range.moveEnd("character", v.length-end);
35686                 range.select();
35687             }
35688         }
35689     },
35690
35691     /**
35692      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35693      * This only takes effect if grow = true, and fires the autosize event.
35694      */
35695     autoSize : function(){
35696         if(!this.grow || !this.rendered){
35697             return;
35698         }
35699         if(!this.metrics){
35700             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35701         }
35702         var el = this.el;
35703         var v = el.dom.value;
35704         var d = document.createElement('div');
35705         d.appendChild(document.createTextNode(v));
35706         v = d.innerHTML;
35707         d = null;
35708         v += "&#160;";
35709         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35710         this.el.setWidth(w);
35711         this.fireEvent("autosize", this, w);
35712     }
35713 });/*
35714  * Based on:
35715  * Ext JS Library 1.1.1
35716  * Copyright(c) 2006-2007, Ext JS, LLC.
35717  *
35718  * Originally Released Under LGPL - original licence link has changed is not relivant.
35719  *
35720  * Fork - LGPL
35721  * <script type="text/javascript">
35722  */
35723  
35724 /**
35725  * @class Roo.form.Hidden
35726  * @extends Roo.form.TextField
35727  * Simple Hidden element used on forms 
35728  * 
35729  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35730  * 
35731  * @constructor
35732  * Creates a new Hidden form element.
35733  * @param {Object} config Configuration options
35734  */
35735
35736
35737
35738 // easy hidden field...
35739 Roo.form.Hidden = function(config){
35740     Roo.form.Hidden.superclass.constructor.call(this, config);
35741 };
35742   
35743 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35744     fieldLabel:      '',
35745     inputType:      'hidden',
35746     width:          50,
35747     allowBlank:     true,
35748     labelSeparator: '',
35749     hidden:         true,
35750     itemCls :       'x-form-item-display-none'
35751
35752
35753 });
35754
35755
35756 /*
35757  * Based on:
35758  * Ext JS Library 1.1.1
35759  * Copyright(c) 2006-2007, Ext JS, LLC.
35760  *
35761  * Originally Released Under LGPL - original licence link has changed is not relivant.
35762  *
35763  * Fork - LGPL
35764  * <script type="text/javascript">
35765  */
35766  
35767 /**
35768  * @class Roo.form.TriggerField
35769  * @extends Roo.form.TextField
35770  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35771  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35772  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35773  * for which you can provide a custom implementation.  For example:
35774  * <pre><code>
35775 var trigger = new Roo.form.TriggerField();
35776 trigger.onTriggerClick = myTriggerFn;
35777 trigger.applyTo('my-field');
35778 </code></pre>
35779  *
35780  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35781  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35782  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35783  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35784  * @constructor
35785  * Create a new TriggerField.
35786  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35787  * to the base TextField)
35788  */
35789 Roo.form.TriggerField = function(config){
35790     this.mimicing = false;
35791     Roo.form.TriggerField.superclass.constructor.call(this, config);
35792 };
35793
35794 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35795     /**
35796      * @cfg {String} triggerClass A CSS class to apply to the trigger
35797      */
35798     /**
35799      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35800      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
35801      */
35802     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
35803     /**
35804      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
35805      */
35806     hideTrigger:false,
35807
35808     /** @cfg {Boolean} grow @hide */
35809     /** @cfg {Number} growMin @hide */
35810     /** @cfg {Number} growMax @hide */
35811
35812     /**
35813      * @hide 
35814      * @method
35815      */
35816     autoSize: Roo.emptyFn,
35817     // private
35818     monitorTab : true,
35819     // private
35820     deferHeight : true,
35821
35822     
35823     actionMode : 'wrap',
35824     // private
35825     onResize : function(w, h){
35826         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
35827         if(typeof w == 'number'){
35828             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
35829         }
35830     },
35831
35832     // private
35833     adjustSize : Roo.BoxComponent.prototype.adjustSize,
35834
35835     // private
35836     getResizeEl : function(){
35837         return this.wrap;
35838     },
35839
35840     // private
35841     getPositionEl : function(){
35842         return this.wrap;
35843     },
35844
35845     // private
35846     alignErrorIcon : function(){
35847         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
35848     },
35849
35850     // private
35851     onRender : function(ct, position){
35852         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
35853         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
35854         this.trigger = this.wrap.createChild(this.triggerConfig ||
35855                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
35856         if(this.hideTrigger){
35857             this.trigger.setDisplayed(false);
35858         }
35859         this.initTrigger();
35860         if(!this.width){
35861             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
35862         }
35863     },
35864
35865     // private
35866     initTrigger : function(){
35867         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
35868         this.trigger.addClassOnOver('x-form-trigger-over');
35869         this.trigger.addClassOnClick('x-form-trigger-click');
35870     },
35871
35872     // private
35873     onDestroy : function(){
35874         if(this.trigger){
35875             this.trigger.removeAllListeners();
35876             this.trigger.remove();
35877         }
35878         if(this.wrap){
35879             this.wrap.remove();
35880         }
35881         Roo.form.TriggerField.superclass.onDestroy.call(this);
35882     },
35883
35884     // private
35885     onFocus : function(){
35886         Roo.form.TriggerField.superclass.onFocus.call(this);
35887         if(!this.mimicing){
35888             this.wrap.addClass('x-trigger-wrap-focus');
35889             this.mimicing = true;
35890             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
35891             if(this.monitorTab){
35892                 this.el.on("keydown", this.checkTab, this);
35893             }
35894         }
35895     },
35896
35897     // private
35898     checkTab : function(e){
35899         if(e.getKey() == e.TAB){
35900             this.triggerBlur();
35901         }
35902     },
35903
35904     // private
35905     onBlur : function(){
35906         // do nothing
35907     },
35908
35909     // private
35910     mimicBlur : function(e, t){
35911         if(!this.wrap.contains(t) && this.validateBlur()){
35912             this.triggerBlur();
35913         }
35914     },
35915
35916     // private
35917     triggerBlur : function(){
35918         this.mimicing = false;
35919         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
35920         if(this.monitorTab){
35921             this.el.un("keydown", this.checkTab, this);
35922         }
35923         this.wrap.removeClass('x-trigger-wrap-focus');
35924         Roo.form.TriggerField.superclass.onBlur.call(this);
35925     },
35926
35927     // private
35928     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
35929     validateBlur : function(e, t){
35930         return true;
35931     },
35932
35933     // private
35934     onDisable : function(){
35935         Roo.form.TriggerField.superclass.onDisable.call(this);
35936         if(this.wrap){
35937             this.wrap.addClass('x-item-disabled');
35938         }
35939     },
35940
35941     // private
35942     onEnable : function(){
35943         Roo.form.TriggerField.superclass.onEnable.call(this);
35944         if(this.wrap){
35945             this.wrap.removeClass('x-item-disabled');
35946         }
35947     },
35948
35949     // private
35950     onShow : function(){
35951         var ae = this.getActionEl();
35952         
35953         if(ae){
35954             ae.dom.style.display = '';
35955             ae.dom.style.visibility = 'visible';
35956         }
35957     },
35958
35959     // private
35960     
35961     onHide : function(){
35962         var ae = this.getActionEl();
35963         ae.dom.style.display = 'none';
35964     },
35965
35966     /**
35967      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
35968      * by an implementing function.
35969      * @method
35970      * @param {EventObject} e
35971      */
35972     onTriggerClick : Roo.emptyFn
35973 });
35974
35975 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
35976 // to be extended by an implementing class.  For an example of implementing this class, see the custom
35977 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
35978 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
35979     initComponent : function(){
35980         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
35981
35982         this.triggerConfig = {
35983             tag:'span', cls:'x-form-twin-triggers', cn:[
35984             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
35985             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
35986         ]};
35987     },
35988
35989     getTrigger : function(index){
35990         return this.triggers[index];
35991     },
35992
35993     initTrigger : function(){
35994         var ts = this.trigger.select('.x-form-trigger', true);
35995         this.wrap.setStyle('overflow', 'hidden');
35996         var triggerField = this;
35997         ts.each(function(t, all, index){
35998             t.hide = function(){
35999                 var w = triggerField.wrap.getWidth();
36000                 this.dom.style.display = 'none';
36001                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36002             };
36003             t.show = function(){
36004                 var w = triggerField.wrap.getWidth();
36005                 this.dom.style.display = '';
36006                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36007             };
36008             var triggerIndex = 'Trigger'+(index+1);
36009
36010             if(this['hide'+triggerIndex]){
36011                 t.dom.style.display = 'none';
36012             }
36013             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36014             t.addClassOnOver('x-form-trigger-over');
36015             t.addClassOnClick('x-form-trigger-click');
36016         }, this);
36017         this.triggers = ts.elements;
36018     },
36019
36020     onTrigger1Click : Roo.emptyFn,
36021     onTrigger2Click : Roo.emptyFn
36022 });/*
36023  * Based on:
36024  * Ext JS Library 1.1.1
36025  * Copyright(c) 2006-2007, Ext JS, LLC.
36026  *
36027  * Originally Released Under LGPL - original licence link has changed is not relivant.
36028  *
36029  * Fork - LGPL
36030  * <script type="text/javascript">
36031  */
36032  
36033 /**
36034  * @class Roo.form.TextArea
36035  * @extends Roo.form.TextField
36036  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36037  * support for auto-sizing.
36038  * @constructor
36039  * Creates a new TextArea
36040  * @param {Object} config Configuration options
36041  */
36042 Roo.form.TextArea = function(config){
36043     Roo.form.TextArea.superclass.constructor.call(this, config);
36044     // these are provided exchanges for backwards compat
36045     // minHeight/maxHeight were replaced by growMin/growMax to be
36046     // compatible with TextField growing config values
36047     if(this.minHeight !== undefined){
36048         this.growMin = this.minHeight;
36049     }
36050     if(this.maxHeight !== undefined){
36051         this.growMax = this.maxHeight;
36052     }
36053 };
36054
36055 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36056     /**
36057      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36058      */
36059     growMin : 60,
36060     /**
36061      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36062      */
36063     growMax: 1000,
36064     /**
36065      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36066      * in the field (equivalent to setting overflow: hidden, defaults to false)
36067      */
36068     preventScrollbars: false,
36069     /**
36070      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36071      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36072      */
36073
36074     // private
36075     onRender : function(ct, position){
36076         if(!this.el){
36077             this.defaultAutoCreate = {
36078                 tag: "textarea",
36079                 style:"width:300px;height:60px;",
36080                 autocomplete: "off"
36081             };
36082         }
36083         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36084         if(this.grow){
36085             this.textSizeEl = Roo.DomHelper.append(document.body, {
36086                 tag: "pre", cls: "x-form-grow-sizer"
36087             });
36088             if(this.preventScrollbars){
36089                 this.el.setStyle("overflow", "hidden");
36090             }
36091             this.el.setHeight(this.growMin);
36092         }
36093     },
36094
36095     onDestroy : function(){
36096         if(this.textSizeEl){
36097             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36098         }
36099         Roo.form.TextArea.superclass.onDestroy.call(this);
36100     },
36101
36102     // private
36103     onKeyUp : function(e){
36104         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36105             this.autoSize();
36106         }
36107     },
36108
36109     /**
36110      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36111      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36112      */
36113     autoSize : function(){
36114         if(!this.grow || !this.textSizeEl){
36115             return;
36116         }
36117         var el = this.el;
36118         var v = el.dom.value;
36119         var ts = this.textSizeEl;
36120
36121         ts.innerHTML = '';
36122         ts.appendChild(document.createTextNode(v));
36123         v = ts.innerHTML;
36124
36125         Roo.fly(ts).setWidth(this.el.getWidth());
36126         if(v.length < 1){
36127             v = "&#160;&#160;";
36128         }else{
36129             if(Roo.isIE){
36130                 v = v.replace(/\n/g, '<p>&#160;</p>');
36131             }
36132             v += "&#160;\n&#160;";
36133         }
36134         ts.innerHTML = v;
36135         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36136         if(h != this.lastHeight){
36137             this.lastHeight = h;
36138             this.el.setHeight(h);
36139             this.fireEvent("autosize", this, h);
36140         }
36141     }
36142 });/*
36143  * Based on:
36144  * Ext JS Library 1.1.1
36145  * Copyright(c) 2006-2007, Ext JS, LLC.
36146  *
36147  * Originally Released Under LGPL - original licence link has changed is not relivant.
36148  *
36149  * Fork - LGPL
36150  * <script type="text/javascript">
36151  */
36152  
36153
36154 /**
36155  * @class Roo.form.NumberField
36156  * @extends Roo.form.TextField
36157  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36158  * @constructor
36159  * Creates a new NumberField
36160  * @param {Object} config Configuration options
36161  */
36162 Roo.form.NumberField = function(config){
36163     Roo.form.NumberField.superclass.constructor.call(this, config);
36164 };
36165
36166 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36167     /**
36168      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36169      */
36170     fieldClass: "x-form-field x-form-num-field",
36171     /**
36172      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36173      */
36174     allowDecimals : true,
36175     /**
36176      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36177      */
36178     decimalSeparator : ".",
36179     /**
36180      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36181      */
36182     decimalPrecision : 2,
36183     /**
36184      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36185      */
36186     allowNegative : true,
36187     /**
36188      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36189      */
36190     minValue : Number.NEGATIVE_INFINITY,
36191     /**
36192      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36193      */
36194     maxValue : Number.MAX_VALUE,
36195     /**
36196      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36197      */
36198     minText : "The minimum value for this field is {0}",
36199     /**
36200      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36201      */
36202     maxText : "The maximum value for this field is {0}",
36203     /**
36204      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36205      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36206      */
36207     nanText : "{0} is not a valid number",
36208
36209     // private
36210     initEvents : function(){
36211         Roo.form.NumberField.superclass.initEvents.call(this);
36212         var allowed = "0123456789";
36213         if(this.allowDecimals){
36214             allowed += this.decimalSeparator;
36215         }
36216         if(this.allowNegative){
36217             allowed += "-";
36218         }
36219         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36220         var keyPress = function(e){
36221             var k = e.getKey();
36222             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36223                 return;
36224             }
36225             var c = e.getCharCode();
36226             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36227                 e.stopEvent();
36228             }
36229         };
36230         this.el.on("keypress", keyPress, this);
36231     },
36232
36233     // private
36234     validateValue : function(value){
36235         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36236             return false;
36237         }
36238         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36239              return true;
36240         }
36241         var num = this.parseValue(value);
36242         if(isNaN(num)){
36243             this.markInvalid(String.format(this.nanText, value));
36244             return false;
36245         }
36246         if(num < this.minValue){
36247             this.markInvalid(String.format(this.minText, this.minValue));
36248             return false;
36249         }
36250         if(num > this.maxValue){
36251             this.markInvalid(String.format(this.maxText, this.maxValue));
36252             return false;
36253         }
36254         return true;
36255     },
36256
36257     getValue : function(){
36258         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36259     },
36260
36261     // private
36262     parseValue : function(value){
36263         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36264         return isNaN(value) ? '' : value;
36265     },
36266
36267     // private
36268     fixPrecision : function(value){
36269         var nan = isNaN(value);
36270         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36271             return nan ? '' : value;
36272         }
36273         return parseFloat(value).toFixed(this.decimalPrecision);
36274     },
36275
36276     setValue : function(v){
36277         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36278     },
36279
36280     // private
36281     decimalPrecisionFcn : function(v){
36282         return Math.floor(v);
36283     },
36284
36285     beforeBlur : function(){
36286         var v = this.parseValue(this.getRawValue());
36287         if(v){
36288             this.setValue(this.fixPrecision(v));
36289         }
36290     }
36291 });/*
36292  * Based on:
36293  * Ext JS Library 1.1.1
36294  * Copyright(c) 2006-2007, Ext JS, LLC.
36295  *
36296  * Originally Released Under LGPL - original licence link has changed is not relivant.
36297  *
36298  * Fork - LGPL
36299  * <script type="text/javascript">
36300  */
36301  
36302 /**
36303  * @class Roo.form.DateField
36304  * @extends Roo.form.TriggerField
36305  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36306 * @constructor
36307 * Create a new DateField
36308 * @param {Object} config
36309  */
36310 Roo.form.DateField = function(config){
36311     Roo.form.DateField.superclass.constructor.call(this, config);
36312     
36313       this.addEvents({
36314          
36315         /**
36316          * @event select
36317          * Fires when a date is selected
36318              * @param {Roo.form.DateField} combo This combo box
36319              * @param {Date} date The date selected
36320              */
36321         'select' : true
36322          
36323     });
36324     
36325     
36326     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36327     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36328     this.ddMatch = null;
36329     if(this.disabledDates){
36330         var dd = this.disabledDates;
36331         var re = "(?:";
36332         for(var i = 0; i < dd.length; i++){
36333             re += dd[i];
36334             if(i != dd.length-1) re += "|";
36335         }
36336         this.ddMatch = new RegExp(re + ")");
36337     }
36338 };
36339
36340 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36341     /**
36342      * @cfg {String} format
36343      * The default date format string which can be overriden for localization support.  The format must be
36344      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36345      */
36346     format : "m/d/y",
36347     /**
36348      * @cfg {String} altFormats
36349      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36350      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36351      */
36352     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36353     /**
36354      * @cfg {Array} disabledDays
36355      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36356      */
36357     disabledDays : null,
36358     /**
36359      * @cfg {String} disabledDaysText
36360      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36361      */
36362     disabledDaysText : "Disabled",
36363     /**
36364      * @cfg {Array} disabledDates
36365      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36366      * expression so they are very powerful. Some examples:
36367      * <ul>
36368      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36369      * <li>["03/08", "09/16"] would disable those days for every year</li>
36370      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36371      * <li>["03/../2006"] would disable every day in March 2006</li>
36372      * <li>["^03"] would disable every day in every March</li>
36373      * </ul>
36374      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36375      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36376      */
36377     disabledDates : null,
36378     /**
36379      * @cfg {String} disabledDatesText
36380      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36381      */
36382     disabledDatesText : "Disabled",
36383     /**
36384      * @cfg {Date/String} minValue
36385      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36386      * valid format (defaults to null).
36387      */
36388     minValue : null,
36389     /**
36390      * @cfg {Date/String} maxValue
36391      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36392      * valid format (defaults to null).
36393      */
36394     maxValue : null,
36395     /**
36396      * @cfg {String} minText
36397      * The error text to display when the date in the cell is before minValue (defaults to
36398      * 'The date in this field must be after {minValue}').
36399      */
36400     minText : "The date in this field must be equal to or after {0}",
36401     /**
36402      * @cfg {String} maxText
36403      * The error text to display when the date in the cell is after maxValue (defaults to
36404      * 'The date in this field must be before {maxValue}').
36405      */
36406     maxText : "The date in this field must be equal to or before {0}",
36407     /**
36408      * @cfg {String} invalidText
36409      * The error text to display when the date in the field is invalid (defaults to
36410      * '{value} is not a valid date - it must be in the format {format}').
36411      */
36412     invalidText : "{0} is not a valid date - it must be in the format {1}",
36413     /**
36414      * @cfg {String} triggerClass
36415      * An additional CSS class used to style the trigger button.  The trigger will always get the
36416      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36417      * which displays a calendar icon).
36418      */
36419     triggerClass : 'x-form-date-trigger',
36420     
36421
36422     /**
36423      * @cfg {bool} useIso
36424      * if enabled, then the date field will use a hidden field to store the 
36425      * real value as iso formated date. default (false)
36426      */ 
36427     useIso : false,
36428     /**
36429      * @cfg {String/Object} autoCreate
36430      * A DomHelper element spec, or true for a default element spec (defaults to
36431      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36432      */ 
36433     // private
36434     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36435     
36436     // private
36437     hiddenField: false,
36438     
36439     onRender : function(ct, position)
36440     {
36441         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36442         if (this.useIso) {
36443             this.el.dom.removeAttribute('name'); 
36444             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36445                     'before', true);
36446             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36447             // prevent input submission
36448             this.hiddenName = this.name;
36449         }
36450             
36451             
36452     },
36453     
36454     // private
36455     validateValue : function(value)
36456     {
36457         value = this.formatDate(value);
36458         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36459             return false;
36460         }
36461         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36462              return true;
36463         }
36464         var svalue = value;
36465         value = this.parseDate(value);
36466         if(!value){
36467             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36468             return false;
36469         }
36470         var time = value.getTime();
36471         if(this.minValue && time < this.minValue.getTime()){
36472             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36473             return false;
36474         }
36475         if(this.maxValue && time > this.maxValue.getTime()){
36476             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36477             return false;
36478         }
36479         if(this.disabledDays){
36480             var day = value.getDay();
36481             for(var i = 0; i < this.disabledDays.length; i++) {
36482                 if(day === this.disabledDays[i]){
36483                     this.markInvalid(this.disabledDaysText);
36484                     return false;
36485                 }
36486             }
36487         }
36488         var fvalue = this.formatDate(value);
36489         if(this.ddMatch && this.ddMatch.test(fvalue)){
36490             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36491             return false;
36492         }
36493         return true;
36494     },
36495
36496     // private
36497     // Provides logic to override the default TriggerField.validateBlur which just returns true
36498     validateBlur : function(){
36499         return !this.menu || !this.menu.isVisible();
36500     },
36501
36502     /**
36503      * Returns the current date value of the date field.
36504      * @return {Date} The date value
36505      */
36506     getValue : function(){
36507         
36508         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36509     },
36510
36511     /**
36512      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36513      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36514      * (the default format used is "m/d/y").
36515      * <br />Usage:
36516      * <pre><code>
36517 //All of these calls set the same date value (May 4, 2006)
36518
36519 //Pass a date object:
36520 var dt = new Date('5/4/06');
36521 dateField.setValue(dt);
36522
36523 //Pass a date string (default format):
36524 dateField.setValue('5/4/06');
36525
36526 //Pass a date string (custom format):
36527 dateField.format = 'Y-m-d';
36528 dateField.setValue('2006-5-4');
36529 </code></pre>
36530      * @param {String/Date} date The date or valid date string
36531      */
36532     setValue : function(date){
36533         if (this.hiddenField) {
36534             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36535         }
36536         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36537     },
36538
36539     // private
36540     parseDate : function(value){
36541         if(!value || value instanceof Date){
36542             return value;
36543         }
36544         var v = Date.parseDate(value, this.format);
36545         if(!v && this.altFormats){
36546             if(!this.altFormatsArray){
36547                 this.altFormatsArray = this.altFormats.split("|");
36548             }
36549             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36550                 v = Date.parseDate(value, this.altFormatsArray[i]);
36551             }
36552         }
36553         return v;
36554     },
36555
36556     // private
36557     formatDate : function(date, fmt){
36558         return (!date || !(date instanceof Date)) ?
36559                date : date.dateFormat(fmt || this.format);
36560     },
36561
36562     // private
36563     menuListeners : {
36564         select: function(m, d){
36565             this.setValue(d);
36566             this.fireEvent('select', this, d);
36567         },
36568         show : function(){ // retain focus styling
36569             this.onFocus();
36570         },
36571         hide : function(){
36572             this.focus.defer(10, this);
36573             var ml = this.menuListeners;
36574             this.menu.un("select", ml.select,  this);
36575             this.menu.un("show", ml.show,  this);
36576             this.menu.un("hide", ml.hide,  this);
36577         }
36578     },
36579
36580     // private
36581     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36582     onTriggerClick : function(){
36583         if(this.disabled){
36584             return;
36585         }
36586         if(this.menu == null){
36587             this.menu = new Roo.menu.DateMenu();
36588         }
36589         Roo.apply(this.menu.picker,  {
36590             showClear: this.allowBlank,
36591             minDate : this.minValue,
36592             maxDate : this.maxValue,
36593             disabledDatesRE : this.ddMatch,
36594             disabledDatesText : this.disabledDatesText,
36595             disabledDays : this.disabledDays,
36596             disabledDaysText : this.disabledDaysText,
36597             format : this.format,
36598             minText : String.format(this.minText, this.formatDate(this.minValue)),
36599             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36600         });
36601         this.menu.on(Roo.apply({}, this.menuListeners, {
36602             scope:this
36603         }));
36604         this.menu.picker.setValue(this.getValue() || new Date());
36605         this.menu.show(this.el, "tl-bl?");
36606     },
36607
36608     beforeBlur : function(){
36609         var v = this.parseDate(this.getRawValue());
36610         if(v){
36611             this.setValue(v);
36612         }
36613     }
36614
36615     /** @cfg {Boolean} grow @hide */
36616     /** @cfg {Number} growMin @hide */
36617     /** @cfg {Number} growMax @hide */
36618     /**
36619      * @hide
36620      * @method autoSize
36621      */
36622 });/*
36623  * Based on:
36624  * Ext JS Library 1.1.1
36625  * Copyright(c) 2006-2007, Ext JS, LLC.
36626  *
36627  * Originally Released Under LGPL - original licence link has changed is not relivant.
36628  *
36629  * Fork - LGPL
36630  * <script type="text/javascript">
36631  */
36632  
36633
36634 /**
36635  * @class Roo.form.ComboBox
36636  * @extends Roo.form.TriggerField
36637  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36638  * @constructor
36639  * Create a new ComboBox.
36640  * @param {Object} config Configuration options
36641  */
36642 Roo.form.ComboBox = function(config){
36643     Roo.form.ComboBox.superclass.constructor.call(this, config);
36644     this.addEvents({
36645         /**
36646          * @event expand
36647          * Fires when the dropdown list is expanded
36648              * @param {Roo.form.ComboBox} combo This combo box
36649              */
36650         'expand' : true,
36651         /**
36652          * @event collapse
36653          * Fires when the dropdown list is collapsed
36654              * @param {Roo.form.ComboBox} combo This combo box
36655              */
36656         'collapse' : true,
36657         /**
36658          * @event beforeselect
36659          * Fires before a list item is selected. Return false to cancel the selection.
36660              * @param {Roo.form.ComboBox} combo This combo box
36661              * @param {Roo.data.Record} record The data record returned from the underlying store
36662              * @param {Number} index The index of the selected item in the dropdown list
36663              */
36664         'beforeselect' : true,
36665         /**
36666          * @event select
36667          * Fires when a list item is selected
36668              * @param {Roo.form.ComboBox} combo This combo box
36669              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36670              * @param {Number} index The index of the selected item in the dropdown list
36671              */
36672         'select' : true,
36673         /**
36674          * @event beforequery
36675          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36676          * The event object passed has these properties:
36677              * @param {Roo.form.ComboBox} combo This combo box
36678              * @param {String} query The query
36679              * @param {Boolean} forceAll true to force "all" query
36680              * @param {Boolean} cancel true to cancel the query
36681              * @param {Object} e The query event object
36682              */
36683         'beforequery': true
36684     });
36685     if(this.transform){
36686         this.allowDomMove = false;
36687         var s = Roo.getDom(this.transform);
36688         if(!this.hiddenName){
36689             this.hiddenName = s.name;
36690         }
36691         if(!this.store){
36692             this.mode = 'local';
36693             var d = [], opts = s.options;
36694             for(var i = 0, len = opts.length;i < len; i++){
36695                 var o = opts[i];
36696                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36697                 if(o.selected) {
36698                     this.value = value;
36699                 }
36700                 d.push([value, o.text]);
36701             }
36702             this.store = new Roo.data.SimpleStore({
36703                 'id': 0,
36704                 fields: ['value', 'text'],
36705                 data : d
36706             });
36707             this.valueField = 'value';
36708             this.displayField = 'text';
36709         }
36710         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36711         if(!this.lazyRender){
36712             this.target = true;
36713             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36714             s.parentNode.removeChild(s); // remove it
36715             this.render(this.el.parentNode);
36716         }else{
36717             s.parentNode.removeChild(s); // remove it
36718         }
36719
36720     }
36721     if (this.store) {
36722         this.store = Roo.factory(this.store, Roo.data);
36723     }
36724     
36725     this.selectedIndex = -1;
36726     if(this.mode == 'local'){
36727         if(config.queryDelay === undefined){
36728             this.queryDelay = 10;
36729         }
36730         if(config.minChars === undefined){
36731             this.minChars = 0;
36732         }
36733     }
36734 };
36735
36736 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36737     /**
36738      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36739      */
36740     /**
36741      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36742      * rendering into an Roo.Editor, defaults to false)
36743      */
36744     /**
36745      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36746      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36747      */
36748     /**
36749      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36750      */
36751     /**
36752      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36753      * the dropdown list (defaults to undefined, with no header element)
36754      */
36755
36756      /**
36757      * @cfg {String/Roo.Template} tpl The template to use to render the output
36758      */
36759      
36760     // private
36761     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36762     /**
36763      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36764      */
36765     listWidth: undefined,
36766     /**
36767      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36768      * mode = 'remote' or 'text' if mode = 'local')
36769      */
36770     displayField: undefined,
36771     /**
36772      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36773      * mode = 'remote' or 'value' if mode = 'local'). 
36774      * Note: use of a valueField requires the user make a selection
36775      * in order for a value to be mapped.
36776      */
36777     valueField: undefined,
36778     /**
36779      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36780      * field's data value (defaults to the underlying DOM element's name)
36781      */
36782     hiddenName: undefined,
36783     /**
36784      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
36785      */
36786     listClass: '',
36787     /**
36788      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
36789      */
36790     selectedClass: 'x-combo-selected',
36791     /**
36792      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36793      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
36794      * which displays a downward arrow icon).
36795      */
36796     triggerClass : 'x-form-arrow-trigger',
36797     /**
36798      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36799      */
36800     shadow:'sides',
36801     /**
36802      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
36803      * anchor positions (defaults to 'tl-bl')
36804      */
36805     listAlign: 'tl-bl?',
36806     /**
36807      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
36808      */
36809     maxHeight: 300,
36810     /**
36811      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
36812      * query specified by the allQuery config option (defaults to 'query')
36813      */
36814     triggerAction: 'query',
36815     /**
36816      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
36817      * (defaults to 4, does not apply if editable = false)
36818      */
36819     minChars : 4,
36820     /**
36821      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
36822      * delay (typeAheadDelay) if it matches a known value (defaults to false)
36823      */
36824     typeAhead: false,
36825     /**
36826      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
36827      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
36828      */
36829     queryDelay: 500,
36830     /**
36831      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
36832      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
36833      */
36834     pageSize: 0,
36835     /**
36836      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
36837      * when editable = true (defaults to false)
36838      */
36839     selectOnFocus:false,
36840     /**
36841      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
36842      */
36843     queryParam: 'query',
36844     /**
36845      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
36846      * when mode = 'remote' (defaults to 'Loading...')
36847      */
36848     loadingText: 'Loading...',
36849     /**
36850      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
36851      */
36852     resizable: false,
36853     /**
36854      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
36855      */
36856     handleHeight : 8,
36857     /**
36858      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
36859      * traditional select (defaults to true)
36860      */
36861     editable: true,
36862     /**
36863      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
36864      */
36865     allQuery: '',
36866     /**
36867      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
36868      */
36869     mode: 'remote',
36870     /**
36871      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
36872      * listWidth has a higher value)
36873      */
36874     minListWidth : 70,
36875     /**
36876      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
36877      * allow the user to set arbitrary text into the field (defaults to false)
36878      */
36879     forceSelection:false,
36880     /**
36881      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
36882      * if typeAhead = true (defaults to 250)
36883      */
36884     typeAheadDelay : 250,
36885     /**
36886      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
36887      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
36888      */
36889     valueNotFoundText : undefined,
36890     /**
36891      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
36892      */
36893     blockFocus : false,
36894     
36895     /**
36896      * @cfg {bool} disableClear Disable showing of clear button.
36897      */
36898     disableClear : false,
36899     
36900     // private
36901     onRender : function(ct, position){
36902         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
36903         if(this.hiddenName){
36904             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
36905                     'before', true);
36906             this.hiddenField.value =
36907                 this.hiddenValue !== undefined ? this.hiddenValue :
36908                 this.value !== undefined ? this.value : '';
36909
36910             // prevent input submission
36911             this.el.dom.removeAttribute('name');
36912         }
36913         if(Roo.isGecko){
36914             this.el.dom.setAttribute('autocomplete', 'off');
36915         }
36916
36917         var cls = 'x-combo-list';
36918
36919         this.list = new Roo.Layer({
36920             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
36921         });
36922
36923         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
36924         this.list.setWidth(lw);
36925         this.list.swallowEvent('mousewheel');
36926         this.assetHeight = 0;
36927
36928         if(this.title){
36929             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
36930             this.assetHeight += this.header.getHeight();
36931         }
36932
36933         this.innerList = this.list.createChild({cls:cls+'-inner'});
36934         this.innerList.on('mouseover', this.onViewOver, this);
36935         this.innerList.on('mousemove', this.onViewMove, this);
36936         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36937         
36938         if(this.allowBlank && !this.pageSize && !this.disableClear){
36939             this.footer = this.list.createChild({cls:cls+'-ft'});
36940             this.pageTb = new Roo.Toolbar(this.footer);
36941            
36942         }
36943         if(this.pageSize){
36944             this.footer = this.list.createChild({cls:cls+'-ft'});
36945             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
36946                     {pageSize: this.pageSize});
36947             
36948         }
36949         
36950         if (this.pageTb && this.allowBlank && !this.disableClear) {
36951             var _this = this;
36952             this.pageTb.add(new Roo.Toolbar.Fill(), {
36953                 cls: 'x-btn-icon x-btn-clear',
36954                 text: '&#160;',
36955                 handler: function()
36956                 {
36957                     _this.collapse();
36958                     _this.clearValue();
36959                     _this.onSelect(false, -1);
36960                 }
36961             });
36962         }
36963         if (this.footer) {
36964             this.assetHeight += this.footer.getHeight();
36965         }
36966         
36967
36968         if(!this.tpl){
36969             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
36970         }
36971
36972         this.view = new Roo.View(this.innerList, this.tpl, {
36973             singleSelect:true, store: this.store, selectedClass: this.selectedClass
36974         });
36975
36976         this.view.on('click', this.onViewClick, this);
36977
36978         this.store.on('beforeload', this.onBeforeLoad, this);
36979         this.store.on('load', this.onLoad, this);
36980         this.store.on('loadexception', this.collapse, this);
36981
36982         if(this.resizable){
36983             this.resizer = new Roo.Resizable(this.list,  {
36984                pinned:true, handles:'se'
36985             });
36986             this.resizer.on('resize', function(r, w, h){
36987                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
36988                 this.listWidth = w;
36989                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
36990                 this.restrictHeight();
36991             }, this);
36992             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
36993         }
36994         if(!this.editable){
36995             this.editable = true;
36996             this.setEditable(false);
36997         }
36998     },
36999
37000     // private
37001     initEvents : function(){
37002         Roo.form.ComboBox.superclass.initEvents.call(this);
37003
37004         this.keyNav = new Roo.KeyNav(this.el, {
37005             "up" : function(e){
37006                 this.inKeyMode = true;
37007                 this.selectPrev();
37008             },
37009
37010             "down" : function(e){
37011                 if(!this.isExpanded()){
37012                     this.onTriggerClick();
37013                 }else{
37014                     this.inKeyMode = true;
37015                     this.selectNext();
37016                 }
37017             },
37018
37019             "enter" : function(e){
37020                 this.onViewClick();
37021                 //return true;
37022             },
37023
37024             "esc" : function(e){
37025                 this.collapse();
37026             },
37027
37028             "tab" : function(e){
37029                 this.onViewClick(false);
37030                 return true;
37031             },
37032
37033             scope : this,
37034
37035             doRelay : function(foo, bar, hname){
37036                 if(hname == 'down' || this.scope.isExpanded()){
37037                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37038                 }
37039                 return true;
37040             },
37041
37042             forceKeyDown: true
37043         });
37044         this.queryDelay = Math.max(this.queryDelay || 10,
37045                 this.mode == 'local' ? 10 : 250);
37046         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37047         if(this.typeAhead){
37048             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37049         }
37050         if(this.editable !== false){
37051             this.el.on("keyup", this.onKeyUp, this);
37052         }
37053         if(this.forceSelection){
37054             this.on('blur', this.doForce, this);
37055         }
37056     },
37057
37058     onDestroy : function(){
37059         if(this.view){
37060             this.view.setStore(null);
37061             this.view.el.removeAllListeners();
37062             this.view.el.remove();
37063             this.view.purgeListeners();
37064         }
37065         if(this.list){
37066             this.list.destroy();
37067         }
37068         if(this.store){
37069             this.store.un('beforeload', this.onBeforeLoad, this);
37070             this.store.un('load', this.onLoad, this);
37071             this.store.un('loadexception', this.collapse, this);
37072         }
37073         Roo.form.ComboBox.superclass.onDestroy.call(this);
37074     },
37075
37076     // private
37077     fireKey : function(e){
37078         if(e.isNavKeyPress() && !this.list.isVisible()){
37079             this.fireEvent("specialkey", this, e);
37080         }
37081     },
37082
37083     // private
37084     onResize: function(w, h){
37085         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37086         if(this.list && this.listWidth === undefined){
37087             var lw = Math.max(w, this.minListWidth);
37088             this.list.setWidth(lw);
37089             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37090         }
37091     },
37092
37093     /**
37094      * Allow or prevent the user from directly editing the field text.  If false is passed,
37095      * the user will only be able to select from the items defined in the dropdown list.  This method
37096      * is the runtime equivalent of setting the 'editable' config option at config time.
37097      * @param {Boolean} value True to allow the user to directly edit the field text
37098      */
37099     setEditable : function(value){
37100         if(value == this.editable){
37101             return;
37102         }
37103         this.editable = value;
37104         if(!value){
37105             this.el.dom.setAttribute('readOnly', true);
37106             this.el.on('mousedown', this.onTriggerClick,  this);
37107             this.el.addClass('x-combo-noedit');
37108         }else{
37109             this.el.dom.setAttribute('readOnly', false);
37110             this.el.un('mousedown', this.onTriggerClick,  this);
37111             this.el.removeClass('x-combo-noedit');
37112         }
37113     },
37114
37115     // private
37116     onBeforeLoad : function(){
37117         if(!this.hasFocus){
37118             return;
37119         }
37120         this.innerList.update(this.loadingText ?
37121                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37122         this.restrictHeight();
37123         this.selectedIndex = -1;
37124     },
37125
37126     // private
37127     onLoad : function(){
37128         if(!this.hasFocus){
37129             return;
37130         }
37131         if(this.store.getCount() > 0){
37132             this.expand();
37133             this.restrictHeight();
37134             if(this.lastQuery == this.allQuery){
37135                 if(this.editable){
37136                     this.el.dom.select();
37137                 }
37138                 if(!this.selectByValue(this.value, true)){
37139                     this.select(0, true);
37140                 }
37141             }else{
37142                 this.selectNext();
37143                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37144                     this.taTask.delay(this.typeAheadDelay);
37145                 }
37146             }
37147         }else{
37148             this.onEmptyResults();
37149         }
37150         //this.el.focus();
37151     },
37152
37153     // private
37154     onTypeAhead : function(){
37155         if(this.store.getCount() > 0){
37156             var r = this.store.getAt(0);
37157             var newValue = r.data[this.displayField];
37158             var len = newValue.length;
37159             var selStart = this.getRawValue().length;
37160             if(selStart != len){
37161                 this.setRawValue(newValue);
37162                 this.selectText(selStart, newValue.length);
37163             }
37164         }
37165     },
37166
37167     // private
37168     onSelect : function(record, index){
37169         if(this.fireEvent('beforeselect', this, record, index) !== false){
37170             this.setFromData(index > -1 ? record.data : false);
37171             this.collapse();
37172             this.fireEvent('select', this, record, index);
37173         }
37174     },
37175
37176     /**
37177      * Returns the currently selected field value or empty string if no value is set.
37178      * @return {String} value The selected value
37179      */
37180     getValue : function(){
37181         if(this.valueField){
37182             return typeof this.value != 'undefined' ? this.value : '';
37183         }else{
37184             return Roo.form.ComboBox.superclass.getValue.call(this);
37185         }
37186     },
37187
37188     /**
37189      * Clears any text/value currently set in the field
37190      */
37191     clearValue : function(){
37192         if(this.hiddenField){
37193             this.hiddenField.value = '';
37194         }
37195         this.value = '';
37196         this.setRawValue('');
37197         this.lastSelectionText = '';
37198         this.applyEmptyText();
37199     },
37200
37201     /**
37202      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37203      * will be displayed in the field.  If the value does not match the data value of an existing item,
37204      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37205      * Otherwise the field will be blank (although the value will still be set).
37206      * @param {String} value The value to match
37207      */
37208     setValue : function(v){
37209         var text = v;
37210         if(this.valueField){
37211             var r = this.findRecord(this.valueField, v);
37212             if(r){
37213                 text = r.data[this.displayField];
37214             }else if(this.valueNotFoundText !== undefined){
37215                 text = this.valueNotFoundText;
37216             }
37217         }
37218         this.lastSelectionText = text;
37219         if(this.hiddenField){
37220             this.hiddenField.value = v;
37221         }
37222         Roo.form.ComboBox.superclass.setValue.call(this, text);
37223         this.value = v;
37224     },
37225     /**
37226      * @property {Object} the last set data for the element
37227      */
37228     
37229     lastData : false,
37230     /**
37231      * Sets the value of the field based on a object which is related to the record format for the store.
37232      * @param {Object} value the value to set as. or false on reset?
37233      */
37234     setFromData : function(o){
37235         var dv = ''; // display value
37236         var vv = ''; // value value..
37237         this.lastData = o;
37238         if (this.displayField) {
37239             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37240         } else {
37241             // this is an error condition!!!
37242             console.log('no value field set for '+ this.name);
37243         }
37244         
37245         if(this.valueField){
37246             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37247         }
37248         if(this.hiddenField){
37249             this.hiddenField.value = vv;
37250             
37251             this.lastSelectionText = dv;
37252             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37253             this.value = vv;
37254             return;
37255         }
37256         // no hidden field.. - we store the value in 'value', but still display
37257         // display field!!!!
37258         this.lastSelectionText = dv;
37259         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37260         this.value = vv;
37261         
37262         
37263     },
37264     // private
37265     reset : function(){
37266         // overridden so that last data is reset..
37267         this.setValue(this.originalValue);
37268         this.clearInvalid();
37269         this.lastData = false;
37270     },
37271     // private
37272     findRecord : function(prop, value){
37273         var record;
37274         if(this.store.getCount() > 0){
37275             this.store.each(function(r){
37276                 if(r.data[prop] == value){
37277                     record = r;
37278                     return false;
37279                 }
37280             });
37281         }
37282         return record;
37283     },
37284
37285     // private
37286     onViewMove : function(e, t){
37287         this.inKeyMode = false;
37288     },
37289
37290     // private
37291     onViewOver : function(e, t){
37292         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37293             return;
37294         }
37295         var item = this.view.findItemFromChild(t);
37296         if(item){
37297             var index = this.view.indexOf(item);
37298             this.select(index, false);
37299         }
37300     },
37301
37302     // private
37303     onViewClick : function(doFocus){
37304         var index = this.view.getSelectedIndexes()[0];
37305         var r = this.store.getAt(index);
37306         if(r){
37307             this.onSelect(r, index);
37308         }
37309         if(doFocus !== false && !this.blockFocus){
37310             this.el.focus();
37311         }
37312     },
37313
37314     // private
37315     restrictHeight : function(){
37316         this.innerList.dom.style.height = '';
37317         var inner = this.innerList.dom;
37318         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37319         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37320         this.list.beginUpdate();
37321         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37322         this.list.alignTo(this.el, this.listAlign);
37323         this.list.endUpdate();
37324     },
37325
37326     // private
37327     onEmptyResults : function(){
37328         this.collapse();
37329     },
37330
37331     /**
37332      * Returns true if the dropdown list is expanded, else false.
37333      */
37334     isExpanded : function(){
37335         return this.list.isVisible();
37336     },
37337
37338     /**
37339      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37340      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37341      * @param {String} value The data value of the item to select
37342      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37343      * selected item if it is not currently in view (defaults to true)
37344      * @return {Boolean} True if the value matched an item in the list, else false
37345      */
37346     selectByValue : function(v, scrollIntoView){
37347         if(v !== undefined && v !== null){
37348             var r = this.findRecord(this.valueField || this.displayField, v);
37349             if(r){
37350                 this.select(this.store.indexOf(r), scrollIntoView);
37351                 return true;
37352             }
37353         }
37354         return false;
37355     },
37356
37357     /**
37358      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37359      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37360      * @param {Number} index The zero-based index of the list item to select
37361      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37362      * selected item if it is not currently in view (defaults to true)
37363      */
37364     select : function(index, scrollIntoView){
37365         this.selectedIndex = index;
37366         this.view.select(index);
37367         if(scrollIntoView !== false){
37368             var el = this.view.getNode(index);
37369             if(el){
37370                 this.innerList.scrollChildIntoView(el, false);
37371             }
37372         }
37373     },
37374
37375     // private
37376     selectNext : function(){
37377         var ct = this.store.getCount();
37378         if(ct > 0){
37379             if(this.selectedIndex == -1){
37380                 this.select(0);
37381             }else if(this.selectedIndex < ct-1){
37382                 this.select(this.selectedIndex+1);
37383             }
37384         }
37385     },
37386
37387     // private
37388     selectPrev : function(){
37389         var ct = this.store.getCount();
37390         if(ct > 0){
37391             if(this.selectedIndex == -1){
37392                 this.select(0);
37393             }else if(this.selectedIndex != 0){
37394                 this.select(this.selectedIndex-1);
37395             }
37396         }
37397     },
37398
37399     // private
37400     onKeyUp : function(e){
37401         if(this.editable !== false && !e.isSpecialKey()){
37402             this.lastKey = e.getKey();
37403             this.dqTask.delay(this.queryDelay);
37404         }
37405     },
37406
37407     // private
37408     validateBlur : function(){
37409         return !this.list || !this.list.isVisible();   
37410     },
37411
37412     // private
37413     initQuery : function(){
37414         this.doQuery(this.getRawValue());
37415     },
37416
37417     // private
37418     doForce : function(){
37419         if(this.el.dom.value.length > 0){
37420             this.el.dom.value =
37421                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37422             this.applyEmptyText();
37423         }
37424     },
37425
37426     /**
37427      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37428      * query allowing the query action to be canceled if needed.
37429      * @param {String} query The SQL query to execute
37430      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37431      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37432      * saved in the current store (defaults to false)
37433      */
37434     doQuery : function(q, forceAll){
37435         if(q === undefined || q === null){
37436             q = '';
37437         }
37438         var qe = {
37439             query: q,
37440             forceAll: forceAll,
37441             combo: this,
37442             cancel:false
37443         };
37444         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37445             return false;
37446         }
37447         q = qe.query;
37448         forceAll = qe.forceAll;
37449         if(forceAll === true || (q.length >= this.minChars)){
37450             if(this.lastQuery != q){
37451                 this.lastQuery = q;
37452                 if(this.mode == 'local'){
37453                     this.selectedIndex = -1;
37454                     if(forceAll){
37455                         this.store.clearFilter();
37456                     }else{
37457                         this.store.filter(this.displayField, q);
37458                     }
37459                     this.onLoad();
37460                 }else{
37461                     this.store.baseParams[this.queryParam] = q;
37462                     this.store.load({
37463                         params: this.getParams(q)
37464                     });
37465                     this.expand();
37466                 }
37467             }else{
37468                 this.selectedIndex = -1;
37469                 this.onLoad();   
37470             }
37471         }
37472     },
37473
37474     // private
37475     getParams : function(q){
37476         var p = {};
37477         //p[this.queryParam] = q;
37478         if(this.pageSize){
37479             p.start = 0;
37480             p.limit = this.pageSize;
37481         }
37482         return p;
37483     },
37484
37485     /**
37486      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37487      */
37488     collapse : function(){
37489         if(!this.isExpanded()){
37490             return;
37491         }
37492         this.list.hide();
37493         Roo.get(document).un('mousedown', this.collapseIf, this);
37494         Roo.get(document).un('mousewheel', this.collapseIf, this);
37495         this.fireEvent('collapse', this);
37496     },
37497
37498     // private
37499     collapseIf : function(e){
37500         if(!e.within(this.wrap) && !e.within(this.list)){
37501             this.collapse();
37502         }
37503     },
37504
37505     /**
37506      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37507      */
37508     expand : function(){
37509         if(this.isExpanded() || !this.hasFocus){
37510             return;
37511         }
37512         this.list.alignTo(this.el, this.listAlign);
37513         this.list.show();
37514         Roo.get(document).on('mousedown', this.collapseIf, this);
37515         Roo.get(document).on('mousewheel', this.collapseIf, this);
37516         this.fireEvent('expand', this);
37517     },
37518
37519     // private
37520     // Implements the default empty TriggerField.onTriggerClick function
37521     onTriggerClick : function(){
37522         if(this.disabled){
37523             return;
37524         }
37525         if(this.isExpanded()){
37526             this.collapse();
37527             if (!this.blockFocus) {
37528                 this.el.focus();
37529             }
37530             
37531         }else {
37532             this.hasFocus = true;
37533             if(this.triggerAction == 'all') {
37534                 this.doQuery(this.allQuery, true);
37535             } else {
37536                 this.doQuery(this.getRawValue());
37537             }
37538             if (!this.blockFocus) {
37539                 this.el.focus();
37540             }
37541         }
37542     }
37543
37544     /** 
37545     * @cfg {Boolean} grow 
37546     * @hide 
37547     */
37548     /** 
37549     * @cfg {Number} growMin 
37550     * @hide 
37551     */
37552     /** 
37553     * @cfg {Number} growMax 
37554     * @hide 
37555     */
37556     /**
37557      * @hide
37558      * @method autoSize
37559      */
37560 });/*
37561  * Based on:
37562  * Ext JS Library 1.1.1
37563  * Copyright(c) 2006-2007, Ext JS, LLC.
37564  *
37565  * Originally Released Under LGPL - original licence link has changed is not relivant.
37566  *
37567  * Fork - LGPL
37568  * <script type="text/javascript">
37569  */
37570 /**
37571  * @class Roo.form.Checkbox
37572  * @extends Roo.form.Field
37573  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37574  * @constructor
37575  * Creates a new Checkbox
37576  * @param {Object} config Configuration options
37577  */
37578 Roo.form.Checkbox = function(config){
37579     Roo.form.Checkbox.superclass.constructor.call(this, config);
37580     this.addEvents({
37581         /**
37582          * @event check
37583          * Fires when the checkbox is checked or unchecked.
37584              * @param {Roo.form.Checkbox} this This checkbox
37585              * @param {Boolean} checked The new checked value
37586              */
37587         check : true
37588     });
37589 };
37590
37591 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37592     /**
37593      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37594      */
37595     focusClass : undefined,
37596     /**
37597      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37598      */
37599     fieldClass: "x-form-field",
37600     /**
37601      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37602      */
37603     checked: false,
37604     /**
37605      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37606      * {tag: "input", type: "checkbox", autocomplete: "off"})
37607      */
37608     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37609     /**
37610      * @cfg {String} boxLabel The text that appears beside the checkbox
37611      */
37612     boxLabel : "",
37613     /**
37614      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37615      */  
37616     inputValue : '1',
37617     /**
37618      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37619      */
37620      valueOff: '0', // value when not checked..
37621
37622     actionMode : 'viewEl', 
37623     //
37624     // private
37625     itemCls : 'x-menu-check-item x-form-item',
37626     groupClass : 'x-menu-group-item',
37627     inputType : 'hidden',
37628     
37629     
37630     inSetChecked: false, // check that we are not calling self...
37631     
37632     inputElement: false, // real input element?
37633     basedOn: false, // ????
37634     
37635     isFormField: true, // not sure where this is needed!!!!
37636
37637     onResize : function(){
37638         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37639         if(!this.boxLabel){
37640             this.el.alignTo(this.wrap, 'c-c');
37641         }
37642     },
37643
37644     initEvents : function(){
37645         Roo.form.Checkbox.superclass.initEvents.call(this);
37646         this.el.on("click", this.onClick,  this);
37647         this.el.on("change", this.onClick,  this);
37648     },
37649
37650
37651     getResizeEl : function(){
37652         return this.wrap;
37653     },
37654
37655     getPositionEl : function(){
37656         return this.wrap;
37657     },
37658
37659     // private
37660     onRender : function(ct, position){
37661         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37662         /*
37663         if(this.inputValue !== undefined){
37664             this.el.dom.value = this.inputValue;
37665         }
37666         */
37667         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37668         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37669         var viewEl = this.wrap.createChild({ 
37670             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37671         this.viewEl = viewEl;   
37672         this.wrap.on('click', this.onClick,  this); 
37673         
37674         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37675         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37676         
37677         
37678         
37679         if(this.boxLabel){
37680             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37681         //    viewEl.on('click', this.onClick,  this); 
37682         }
37683         //if(this.checked){
37684             this.setChecked(this.checked);
37685         //}else{
37686             //this.checked = this.el.dom;
37687         //}
37688
37689     },
37690
37691     // private
37692     initValue : Roo.emptyFn,
37693
37694     /**
37695      * Returns the checked state of the checkbox.
37696      * @return {Boolean} True if checked, else false
37697      */
37698     getValue : function(){
37699         if(this.el){
37700             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
37701         }
37702         return this.valueOff;
37703         
37704     },
37705
37706         // private
37707     onClick : function(){ 
37708         this.setChecked(!this.checked);
37709
37710         //if(this.el.dom.checked != this.checked){
37711         //    this.setValue(this.el.dom.checked);
37712        // }
37713     },
37714
37715     /**
37716      * Sets the checked state of the checkbox.
37717      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
37718      */
37719     setValue : function(v,suppressEvent){
37720         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
37721         //if(this.el && this.el.dom){
37722         //    this.el.dom.checked = this.checked;
37723         //    this.el.dom.defaultChecked = this.checked;
37724         //}
37725         this.setChecked(v === this.inputValue);
37726         //this.fireEvent("check", this, this.checked);
37727     },
37728     // private..
37729     setChecked : function(state,suppressEvent)
37730     {
37731         if (this.inSetChecked) {
37732             this.checked = state;
37733             return;
37734         }
37735         
37736     
37737         if(this.wrap){
37738             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
37739         }
37740         this.checked = state;
37741         if(suppressEvent !== true){
37742             this.fireEvent('checkchange', this, state);
37743         }
37744         this.inSetChecked = true;
37745         this.el.dom.value = state ? this.inputValue : this.valueOff;
37746         this.inSetChecked = false;
37747         
37748     },
37749     // handle setting of hidden value by some other method!!?!?
37750     setFromHidden: function()
37751     {
37752         if(!this.el){
37753             return;
37754         }
37755         //console.log("SET FROM HIDDEN");
37756         //alert('setFrom hidden');
37757         this.setValue(this.el.dom.value);
37758     },
37759     
37760     onDestroy : function()
37761     {
37762         if(this.viewEl){
37763             Roo.get(this.viewEl).remove();
37764         }
37765          
37766         Roo.form.Checkbox.superclass.onDestroy.call(this);
37767     }
37768
37769 });/*
37770  * Based on:
37771  * Ext JS Library 1.1.1
37772  * Copyright(c) 2006-2007, Ext JS, LLC.
37773  *
37774  * Originally Released Under LGPL - original licence link has changed is not relivant.
37775  *
37776  * Fork - LGPL
37777  * <script type="text/javascript">
37778  */
37779  
37780 /**
37781  * @class Roo.form.Radio
37782  * @extends Roo.form.Checkbox
37783  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
37784  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
37785  * @constructor
37786  * Creates a new Radio
37787  * @param {Object} config Configuration options
37788  */
37789 Roo.form.Radio = function(){
37790     Roo.form.Radio.superclass.constructor.apply(this, arguments);
37791 };
37792 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
37793     inputType: 'radio',
37794
37795     /**
37796      * If this radio is part of a group, it will return the selected value
37797      * @return {String}
37798      */
37799     getGroupValue : function(){
37800         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
37801     }
37802 });//<script type="text/javascript">
37803
37804 /*
37805  * Ext JS Library 1.1.1
37806  * Copyright(c) 2006-2007, Ext JS, LLC.
37807  * licensing@extjs.com
37808  * 
37809  * http://www.extjs.com/license
37810  */
37811  
37812  /*
37813   * 
37814   * Known bugs:
37815   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
37816   * - IE ? - no idea how much works there.
37817   * 
37818   * 
37819   * 
37820   */
37821  
37822
37823 /**
37824  * @class Ext.form.HtmlEditor
37825  * @extends Ext.form.Field
37826  * Provides a lightweight HTML Editor component.
37827  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
37828  * 
37829  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
37830  * supported by this editor.</b><br/><br/>
37831  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
37832  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
37833  */
37834 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
37835       /**
37836      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
37837      */
37838     toolbars : false,
37839     /**
37840      * @cfg {String} createLinkText The default text for the create link prompt
37841      */
37842     createLinkText : 'Please enter the URL for the link:',
37843     /**
37844      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
37845      */
37846     defaultLinkValue : 'http:/'+'/',
37847    
37848     
37849     // id of frame..
37850     frameId: false,
37851     
37852     // private properties
37853     validationEvent : false,
37854     deferHeight: true,
37855     initialized : false,
37856     activated : false,
37857     sourceEditMode : false,
37858     onFocus : Roo.emptyFn,
37859     iframePad:3,
37860     hideMode:'offsets',
37861     defaultAutoCreate : {
37862         tag: "textarea",
37863         style:"width:500px;height:300px;",
37864         autocomplete: "off"
37865     },
37866
37867     // private
37868     initComponent : function(){
37869         this.addEvents({
37870             /**
37871              * @event initialize
37872              * Fires when the editor is fully initialized (including the iframe)
37873              * @param {HtmlEditor} this
37874              */
37875             initialize: true,
37876             /**
37877              * @event activate
37878              * Fires when the editor is first receives the focus. Any insertion must wait
37879              * until after this event.
37880              * @param {HtmlEditor} this
37881              */
37882             activate: true,
37883              /**
37884              * @event beforesync
37885              * Fires before the textarea is updated with content from the editor iframe. Return false
37886              * to cancel the sync.
37887              * @param {HtmlEditor} this
37888              * @param {String} html
37889              */
37890             beforesync: true,
37891              /**
37892              * @event beforepush
37893              * Fires before the iframe editor is updated with content from the textarea. Return false
37894              * to cancel the push.
37895              * @param {HtmlEditor} this
37896              * @param {String} html
37897              */
37898             beforepush: true,
37899              /**
37900              * @event sync
37901              * Fires when the textarea is updated with content from the editor iframe.
37902              * @param {HtmlEditor} this
37903              * @param {String} html
37904              */
37905             sync: true,
37906              /**
37907              * @event push
37908              * Fires when the iframe editor is updated with content from the textarea.
37909              * @param {HtmlEditor} this
37910              * @param {String} html
37911              */
37912             push: true,
37913              /**
37914              * @event editmodechange
37915              * Fires when the editor switches edit modes
37916              * @param {HtmlEditor} this
37917              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
37918              */
37919             editmodechange: true,
37920             /**
37921              * @event editorevent
37922              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
37923              * @param {HtmlEditor} this
37924              */
37925             editorevent: true
37926         })
37927     },
37928
37929     /**
37930      * Protected method that will not generally be called directly. It
37931      * is called when the editor creates its toolbar. Override this method if you need to
37932      * add custom toolbar buttons.
37933      * @param {HtmlEditor} editor
37934      */
37935     createToolbar : function(editor){
37936         if (!editor.toolbars || !editor.toolbars.length) {
37937             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
37938         }
37939         
37940         for (var i =0 ; i < editor.toolbars.length;i++) {
37941             editor.toolbars[i].init(editor);
37942         }
37943          
37944         
37945     },
37946
37947     /**
37948      * Protected method that will not generally be called directly. It
37949      * is called when the editor initializes the iframe with HTML contents. Override this method if you
37950      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
37951      */
37952     getDocMarkup : function(){
37953         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
37954     },
37955
37956     // private
37957     onRender : function(ct, position){
37958         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
37959         this.el.dom.style.border = '0 none';
37960         this.el.dom.setAttribute('tabIndex', -1);
37961         this.el.addClass('x-hidden');
37962         if(Roo.isIE){ // fix IE 1px bogus margin
37963             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
37964         }
37965         this.wrap = this.el.wrap({
37966             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
37967         });
37968
37969         this.frameId = Roo.id();
37970         this.createToolbar(this);
37971         
37972         
37973         
37974         
37975       
37976         
37977         var iframe = this.wrap.createChild({
37978             tag: 'iframe',
37979             id: this.frameId,
37980             name: this.frameId,
37981             frameBorder : 'no',
37982             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
37983         });
37984         
37985        // console.log(iframe);
37986         //this.wrap.dom.appendChild(iframe);
37987
37988         this.iframe = iframe.dom;
37989
37990          this.assignDocWin();
37991         
37992         this.doc.designMode = 'on';
37993        
37994         this.doc.open();
37995         this.doc.write(this.getDocMarkup());
37996         this.doc.close();
37997
37998         
37999         var task = { // must defer to wait for browser to be ready
38000             run : function(){
38001                 //console.log("run task?" + this.doc.readyState);
38002                 this.assignDocWin();
38003                 if(this.doc.body || this.doc.readyState == 'complete'){
38004                     try {
38005                         
38006                        
38007                         this.doc.designMode="on";
38008                     } catch (e) {
38009                         return;
38010                     }
38011                     Roo.TaskMgr.stop(task);
38012                     this.initEditor.defer(10, this);
38013                 }
38014             },
38015             interval : 10,
38016             duration:10000,
38017             scope: this
38018         };
38019         Roo.TaskMgr.start(task);
38020
38021         if(!this.width){
38022             this.setSize(this.el.getSize());
38023         }
38024     },
38025
38026     // private
38027     onResize : function(w, h){
38028         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38029         if(this.el && this.iframe){
38030             if(typeof w == 'number'){
38031                 var aw = w - this.wrap.getFrameWidth('lr');
38032                 this.el.setWidth(this.adjustWidth('textarea', aw));
38033                 this.iframe.style.width = aw + 'px';
38034             }
38035             if(typeof h == 'number'){
38036                 var tbh = 0;
38037                 for (var i =0; i < this.toolbars.length;i++) {
38038                     // fixme - ask toolbars for heights?
38039                     tbh += this.toolbars[i].tb.el.getHeight();
38040                 }
38041                 
38042                 
38043                 
38044                 
38045                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38046                 this.el.setHeight(this.adjustWidth('textarea', ah));
38047                 this.iframe.style.height = ah + 'px';
38048                 if(this.doc){
38049                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38050                 }
38051             }
38052         }
38053     },
38054
38055     /**
38056      * Toggles the editor between standard and source edit mode.
38057      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38058      */
38059     toggleSourceEdit : function(sourceEditMode){
38060         
38061         this.sourceEditMode = sourceEditMode === true;
38062         
38063         if(this.sourceEditMode){
38064           
38065             this.syncValue();
38066             this.iframe.className = 'x-hidden';
38067             this.el.removeClass('x-hidden');
38068             this.el.dom.removeAttribute('tabIndex');
38069             this.el.focus();
38070         }else{
38071              
38072             this.pushValue();
38073             this.iframe.className = '';
38074             this.el.addClass('x-hidden');
38075             this.el.dom.setAttribute('tabIndex', -1);
38076             this.deferFocus();
38077         }
38078         this.setSize(this.wrap.getSize());
38079         this.fireEvent('editmodechange', this, this.sourceEditMode);
38080     },
38081
38082     // private used internally
38083     createLink : function(){
38084         var url = prompt(this.createLinkText, this.defaultLinkValue);
38085         if(url && url != 'http:/'+'/'){
38086             this.relayCmd('createlink', url);
38087         }
38088     },
38089
38090     // private (for BoxComponent)
38091     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38092
38093     // private (for BoxComponent)
38094     getResizeEl : function(){
38095         return this.wrap;
38096     },
38097
38098     // private (for BoxComponent)
38099     getPositionEl : function(){
38100         return this.wrap;
38101     },
38102
38103     // private
38104     initEvents : function(){
38105         this.originalValue = this.getValue();
38106     },
38107
38108     /**
38109      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38110      * @method
38111      */
38112     markInvalid : Roo.emptyFn,
38113     /**
38114      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38115      * @method
38116      */
38117     clearInvalid : Roo.emptyFn,
38118
38119     setValue : function(v){
38120         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38121         this.pushValue();
38122     },
38123
38124     /**
38125      * Protected method that will not generally be called directly. If you need/want
38126      * custom HTML cleanup, this is the method you should override.
38127      * @param {String} html The HTML to be cleaned
38128      * return {String} The cleaned HTML
38129      */
38130     cleanHtml : function(html){
38131         html = String(html);
38132         if(html.length > 5){
38133             if(Roo.isSafari){ // strip safari nonsense
38134                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38135             }
38136         }
38137         if(html == '&nbsp;'){
38138             html = '';
38139         }
38140         return html;
38141     },
38142
38143     /**
38144      * Protected method that will not generally be called directly. Syncs the contents
38145      * of the editor iframe with the textarea.
38146      */
38147     syncValue : function(){
38148         if(this.initialized){
38149             var bd = (this.doc.body || this.doc.documentElement);
38150             var html = bd.innerHTML;
38151             if(Roo.isSafari){
38152                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38153                 var m = bs.match(/text-align:(.*?);/i);
38154                 if(m && m[1]){
38155                     html = '<div style="'+m[0]+'">' + html + '</div>';
38156                 }
38157             }
38158             html = this.cleanHtml(html);
38159             if(this.fireEvent('beforesync', this, html) !== false){
38160                 this.el.dom.value = html;
38161                 this.fireEvent('sync', this, html);
38162             }
38163         }
38164     },
38165
38166     /**
38167      * Protected method that will not generally be called directly. Pushes the value of the textarea
38168      * into the iframe editor.
38169      */
38170     pushValue : function(){
38171         if(this.initialized){
38172             var v = this.el.dom.value;
38173             if(v.length < 1){
38174                 v = '&#160;';
38175             }
38176             if(this.fireEvent('beforepush', this, v) !== false){
38177                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38178                 this.fireEvent('push', this, v);
38179             }
38180         }
38181     },
38182
38183     // private
38184     deferFocus : function(){
38185         this.focus.defer(10, this);
38186     },
38187
38188     // doc'ed in Field
38189     focus : function(){
38190         if(this.win && !this.sourceEditMode){
38191             this.win.focus();
38192         }else{
38193             this.el.focus();
38194         }
38195     },
38196     
38197     assignDocWin: function()
38198     {
38199         var iframe = this.iframe;
38200         
38201          if(Roo.isIE){
38202             this.doc = iframe.contentWindow.document;
38203             this.win = iframe.contentWindow;
38204         } else {
38205             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38206             this.win = Roo.get(this.frameId).dom.contentWindow;
38207         }
38208     },
38209     
38210     // private
38211     initEditor : function(){
38212         //console.log("INIT EDITOR");
38213         this.assignDocWin();
38214         
38215         
38216         
38217         this.doc.designMode="on";
38218         this.doc.open();
38219         this.doc.write(this.getDocMarkup());
38220         this.doc.close();
38221         
38222         var dbody = (this.doc.body || this.doc.documentElement);
38223         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38224         // this copies styles from the containing element into thsi one..
38225         // not sure why we need all of this..
38226         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38227         ss['background-attachment'] = 'fixed'; // w3c
38228         dbody.bgProperties = 'fixed'; // ie
38229         Roo.DomHelper.applyStyles(dbody, ss);
38230         Roo.EventManager.on(this.doc, {
38231             'mousedown': this.onEditorEvent,
38232             'dblclick': this.onEditorEvent,
38233             'click': this.onEditorEvent,
38234             'keyup': this.onEditorEvent,
38235             buffer:100,
38236             scope: this
38237         });
38238         if(Roo.isGecko){
38239             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
38240         }
38241         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38242             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38243         }
38244         this.initialized = true;
38245
38246         this.fireEvent('initialize', this);
38247         this.pushValue();
38248     },
38249
38250     // private
38251     onDestroy : function(){
38252         
38253         
38254         
38255         if(this.rendered){
38256             
38257             for (var i =0; i < this.toolbars.length;i++) {
38258                 // fixme - ask toolbars for heights?
38259                 this.toolbars[i].onDestroy();
38260             }
38261             
38262             this.wrap.dom.innerHTML = '';
38263             this.wrap.remove();
38264         }
38265     },
38266
38267     // private
38268     onFirstFocus : function(){
38269         
38270         this.assignDocWin();
38271         
38272         
38273         this.activated = true;
38274         for (var i =0; i < this.toolbars.length;i++) {
38275             this.toolbars[i].onFirstFocus();
38276         }
38277        
38278         if(Roo.isGecko){ // prevent silly gecko errors
38279             this.win.focus();
38280             var s = this.win.getSelection();
38281             if(!s.focusNode || s.focusNode.nodeType != 3){
38282                 var r = s.getRangeAt(0);
38283                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38284                 r.collapse(true);
38285                 this.deferFocus();
38286             }
38287             try{
38288                 this.execCmd('useCSS', true);
38289                 this.execCmd('styleWithCSS', false);
38290             }catch(e){}
38291         }
38292         this.fireEvent('activate', this);
38293     },
38294
38295     // private
38296     adjustFont: function(btn){
38297         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38298         //if(Roo.isSafari){ // safari
38299         //    adjust *= 2;
38300        // }
38301         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38302         if(Roo.isSafari){ // safari
38303             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38304             v =  (v < 10) ? 10 : v;
38305             v =  (v > 48) ? 48 : v;
38306             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38307             
38308         }
38309         
38310         
38311         v = Math.max(1, v+adjust);
38312         
38313         this.execCmd('FontSize', v  );
38314     },
38315
38316     onEditorEvent : function(e){
38317         this.fireEvent('editorevent', this, e);
38318       //  this.updateToolbar();
38319         this.syncValue();
38320     },
38321
38322     insertTag : function(tg)
38323     {
38324         // could be a bit smarter... -> wrap the current selected tRoo..
38325         
38326         this.execCmd("formatblock",   tg);
38327         
38328     },
38329     
38330     insertText : function(txt)
38331     {
38332         
38333         
38334         range = this.createRange();
38335         range.deleteContents();
38336                //alert(Sender.getAttribute('label'));
38337                
38338         range.insertNode(this.doc.createTextNode(txt));
38339     } ,
38340     
38341     // private
38342     relayBtnCmd : function(btn){
38343         this.relayCmd(btn.cmd);
38344     },
38345
38346     /**
38347      * Executes a Midas editor command on the editor document and performs necessary focus and
38348      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38349      * @param {String} cmd The Midas command
38350      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38351      */
38352     relayCmd : function(cmd, value){
38353         this.win.focus();
38354         this.execCmd(cmd, value);
38355         this.fireEvent('editorevent', this);
38356         //this.updateToolbar();
38357         this.deferFocus();
38358     },
38359
38360     /**
38361      * Executes a Midas editor command directly on the editor document.
38362      * For visual commands, you should use {@link #relayCmd} instead.
38363      * <b>This should only be called after the editor is initialized.</b>
38364      * @param {String} cmd The Midas command
38365      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38366      */
38367     execCmd : function(cmd, value){
38368         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38369         this.syncValue();
38370     },
38371
38372     // private
38373     applyCommand : function(e){
38374         if(e.ctrlKey){
38375             var c = e.getCharCode(), cmd;
38376             if(c > 0){
38377                 c = String.fromCharCode(c);
38378                 switch(c){
38379                     case 'b':
38380                         cmd = 'bold';
38381                     break;
38382                     case 'i':
38383                         cmd = 'italic';
38384                     break;
38385                     case 'u':
38386                         cmd = 'underline';
38387                     break;
38388                 }
38389                 if(cmd){
38390                     this.win.focus();
38391                     this.execCmd(cmd);
38392                     this.deferFocus();
38393                     e.preventDefault();
38394                 }
38395             }
38396         }
38397     },
38398
38399     /**
38400      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38401      * to insert tRoo.
38402      * @param {String} text
38403      */
38404     insertAtCursor : function(text){
38405         if(!this.activated){
38406             return;
38407         }
38408         if(Roo.isIE){
38409             this.win.focus();
38410             var r = this.doc.selection.createRange();
38411             if(r){
38412                 r.collapse(true);
38413                 r.pasteHTML(text);
38414                 this.syncValue();
38415                 this.deferFocus();
38416             }
38417         }else if(Roo.isGecko || Roo.isOpera){
38418             this.win.focus();
38419             this.execCmd('InsertHTML', text);
38420             this.deferFocus();
38421         }else if(Roo.isSafari){
38422             this.execCmd('InsertText', text);
38423             this.deferFocus();
38424         }
38425     },
38426
38427     // private
38428     fixKeys : function(){ // load time branching for fastest keydown performance
38429         if(Roo.isIE){
38430             return function(e){
38431                 var k = e.getKey(), r;
38432                 if(k == e.TAB){
38433                     e.stopEvent();
38434                     r = this.doc.selection.createRange();
38435                     if(r){
38436                         r.collapse(true);
38437                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38438                         this.deferFocus();
38439                     }
38440                 }else if(k == e.ENTER){
38441                     r = this.doc.selection.createRange();
38442                     if(r){
38443                         var target = r.parentElement();
38444                         if(!target || target.tagName.toLowerCase() != 'li'){
38445                             e.stopEvent();
38446                             r.pasteHTML('<br />');
38447                             r.collapse(false);
38448                             r.select();
38449                         }
38450                     }
38451                 }
38452             };
38453         }else if(Roo.isOpera){
38454             return function(e){
38455                 var k = e.getKey();
38456                 if(k == e.TAB){
38457                     e.stopEvent();
38458                     this.win.focus();
38459                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38460                     this.deferFocus();
38461                 }
38462             };
38463         }else if(Roo.isSafari){
38464             return function(e){
38465                 var k = e.getKey();
38466                 if(k == e.TAB){
38467                     e.stopEvent();
38468                     this.execCmd('InsertText','\t');
38469                     this.deferFocus();
38470                 }
38471              };
38472         }
38473     }(),
38474     
38475     getAllAncestors: function()
38476     {
38477         var p = this.getSelectedNode();
38478         var a = [];
38479         if (!p) {
38480             a.push(p); // push blank onto stack..
38481             p = this.getParentElement();
38482         }
38483         
38484         
38485         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38486             a.push(p);
38487             p = p.parentNode;
38488         }
38489         a.push(this.doc.body);
38490         return a;
38491     },
38492     lastSel : false,
38493     lastSelNode : false,
38494     
38495     
38496     getSelection : function() 
38497     {
38498         this.assignDocWin();
38499         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38500     },
38501     
38502     getSelectedNode: function() 
38503     {
38504         // this may only work on Gecko!!!
38505         
38506         // should we cache this!!!!
38507         
38508         
38509         
38510          
38511         var range = this.createRange(this.getSelection());
38512         
38513         if (Roo.isIE) {
38514             var parent = range.parentElement();
38515             while (true) {
38516                 var testRange = range.duplicate();
38517                 testRange.moveToElementText(parent);
38518                 if (testRange.inRange(range)) {
38519                     break;
38520                 }
38521                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38522                     break;
38523                 }
38524                 parent = parent.parentElement;
38525             }
38526             return parent;
38527         }
38528         
38529         
38530         var ar = range.endContainer.childNodes;
38531         if (!ar.length) {
38532             ar = range.commonAncestorContainer.childNodes;
38533             //alert(ar.length);
38534         }
38535         var nodes = [];
38536         var other_nodes = [];
38537         var has_other_nodes = false;
38538         for (var i=0;i<ar.length;i++) {
38539             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38540                 continue;
38541             }
38542             // fullly contained node.
38543             
38544             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38545                 nodes.push(ar[i]);
38546                 continue;
38547             }
38548             
38549             // probably selected..
38550             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38551                 other_nodes.push(ar[i]);
38552                 continue;
38553             }
38554             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38555                 continue;
38556             }
38557             
38558             
38559             has_other_nodes = true;
38560         }
38561         if (!nodes.length && other_nodes.length) {
38562             nodes= other_nodes;
38563         }
38564         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38565             return false;
38566         }
38567         
38568         return nodes[0];
38569     },
38570     createRange: function(sel)
38571     {
38572         // this has strange effects when using with 
38573         // top toolbar - not sure if it's a great idea.
38574         //this.editor.contentWindow.focus();
38575         if (typeof sel != "undefined") {
38576             try {
38577                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38578             } catch(e) {
38579                 return this.doc.createRange();
38580             }
38581         } else {
38582             return this.doc.createRange();
38583         }
38584     },
38585     getParentElement: function()
38586     {
38587         
38588         this.assignDocWin();
38589         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38590         
38591         var range = this.createRange(sel);
38592          
38593         try {
38594             var p = range.commonAncestorContainer;
38595             while (p.nodeType == 3) { // text node
38596                 p = p.parentNode;
38597             }
38598             return p;
38599         } catch (e) {
38600             return null;
38601         }
38602     
38603     },
38604     
38605     
38606     
38607     // BC Hacks - cause I cant work out what i was trying to do..
38608     rangeIntersectsNode : function(range, node)
38609     {
38610         var nodeRange = node.ownerDocument.createRange();
38611         try {
38612             nodeRange.selectNode(node);
38613         }
38614         catch (e) {
38615             nodeRange.selectNodeContents(node);
38616         }
38617
38618         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38619                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38620     },
38621     rangeCompareNode : function(range, node) {
38622         var nodeRange = node.ownerDocument.createRange();
38623         try {
38624             nodeRange.selectNode(node);
38625         } catch (e) {
38626             nodeRange.selectNodeContents(node);
38627         }
38628         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38629         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38630
38631         if (nodeIsBefore && !nodeIsAfter)
38632             return 0;
38633         if (!nodeIsBefore && nodeIsAfter)
38634             return 1;
38635         if (nodeIsBefore && nodeIsAfter)
38636             return 2;
38637
38638         return 3;
38639     }
38640
38641     
38642     
38643     // hide stuff that is not compatible
38644     /**
38645      * @event blur
38646      * @hide
38647      */
38648     /**
38649      * @event change
38650      * @hide
38651      */
38652     /**
38653      * @event focus
38654      * @hide
38655      */
38656     /**
38657      * @event specialkey
38658      * @hide
38659      */
38660     /**
38661      * @cfg {String} fieldClass @hide
38662      */
38663     /**
38664      * @cfg {String} focusClass @hide
38665      */
38666     /**
38667      * @cfg {String} autoCreate @hide
38668      */
38669     /**
38670      * @cfg {String} inputType @hide
38671      */
38672     /**
38673      * @cfg {String} invalidClass @hide
38674      */
38675     /**
38676      * @cfg {String} invalidText @hide
38677      */
38678     /**
38679      * @cfg {String} msgFx @hide
38680      */
38681     /**
38682      * @cfg {String} validateOnBlur @hide
38683      */
38684 });// <script type="text/javascript">
38685 /*
38686  * Based on
38687  * Ext JS Library 1.1.1
38688  * Copyright(c) 2006-2007, Ext JS, LLC.
38689  *  
38690  
38691  */
38692
38693 /**
38694  * @class Roo.form.HtmlEditorToolbar1
38695  * Basic Toolbar
38696  * 
38697  * Usage:
38698  *
38699  new Roo.form.HtmlEditor({
38700     ....
38701     toolbars : [
38702         new Roo.form.HtmlEditorToolbar1({
38703             disable : { fonts: 1 , format: 1, ..., ... , ...],
38704             btns : [ .... ]
38705         })
38706     }
38707      
38708  * 
38709  * @cfg {Object} disable List of elements to disable..
38710  * @cfg {Array} btns List of additional buttons.
38711  * 
38712  * 
38713  * NEEDS Extra CSS? 
38714  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
38715  */
38716  
38717 Roo.form.HtmlEditor.ToolbarStandard = function(config)
38718 {
38719     
38720     Roo.apply(this, config);
38721     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
38722     // dont call parent... till later.
38723 }
38724
38725 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
38726     
38727     tb: false,
38728     
38729     rendered: false,
38730     
38731     editor : false,
38732     /**
38733      * @cfg {Object} disable  List of toolbar elements to disable
38734          
38735      */
38736     disable : false,
38737       /**
38738      * @cfg {Array} fontFamilies An array of available font families
38739      */
38740     fontFamilies : [
38741         'Arial',
38742         'Courier New',
38743         'Tahoma',
38744         'Times New Roman',
38745         'Verdana'
38746     ],
38747     
38748     specialChars : [
38749            "&#169;",
38750           "&#174;",     
38751           "&#8482;",    
38752           "&#163;" ,    
38753          // "&#8212;",    
38754           "&#8230;",    
38755           "&#247;" ,    
38756         //  "&#225;" ,     ?? a acute?
38757            "&#8364;"    , //Euro
38758        //   "&#8220;"    ,
38759         //  "&#8221;"    ,
38760         //  "&#8226;"    ,
38761           "&#176;"  //   , // degrees
38762
38763          // "&#233;"     , // e ecute
38764          // "&#250;"     , // u ecute?
38765     ],
38766     inputElements : [ 
38767             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
38768             "input:submit", "input:button", "select", "textarea", "label" ],
38769     formats : [
38770         ["p"] ,  
38771         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
38772         ["pre"],[ "code"], 
38773         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
38774     ],
38775      /**
38776      * @cfg {String} defaultFont default font to use.
38777      */
38778     defaultFont: 'tahoma',
38779    
38780     fontSelect : false,
38781     
38782     
38783     formatCombo : false,
38784     
38785     init : function(editor)
38786     {
38787         this.editor = editor;
38788         
38789         
38790         var fid = editor.frameId;
38791         var etb = this;
38792         function btn(id, toggle, handler){
38793             var xid = fid + '-'+ id ;
38794             return {
38795                 id : xid,
38796                 cmd : id,
38797                 cls : 'x-btn-icon x-edit-'+id,
38798                 enableToggle:toggle !== false,
38799                 scope: editor, // was editor...
38800                 handler:handler||editor.relayBtnCmd,
38801                 clickEvent:'mousedown',
38802                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
38803                 tabIndex:-1
38804             };
38805         }
38806         
38807         
38808         
38809         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
38810         this.tb = tb;
38811          // stop form submits
38812         tb.el.on('click', function(e){
38813             e.preventDefault(); // what does this do?
38814         });
38815
38816         if(!this.disable.font && !Roo.isSafari){
38817             /* why no safari for fonts
38818             editor.fontSelect = tb.el.createChild({
38819                 tag:'select',
38820                 tabIndex: -1,
38821                 cls:'x-font-select',
38822                 html: editor.createFontOptions()
38823             });
38824             editor.fontSelect.on('change', function(){
38825                 var font = editor.fontSelect.dom.value;
38826                 editor.relayCmd('fontname', font);
38827                 editor.deferFocus();
38828             }, editor);
38829             tb.add(
38830                 editor.fontSelect.dom,
38831                 '-'
38832             );
38833             */
38834         };
38835         if(!this.disable.formats){
38836             this.formatCombo = new Roo.form.ComboBox({
38837                 store: new Roo.data.SimpleStore({
38838                     id : 'tag',
38839                     fields: ['tag'],
38840                     data : this.formats // from states.js
38841                 }),
38842                 blockFocus : true,
38843                 //autoCreate : {tag: "div",  size: "20"},
38844                 displayField:'tag',
38845                 typeAhead: false,
38846                 mode: 'local',
38847                 editable : false,
38848                 triggerAction: 'all',
38849                 emptyText:'Add tag',
38850                 selectOnFocus:true,
38851                 width:135,
38852                 listeners : {
38853                     'select': function(c, r, i) {
38854                         editor.insertTag(r.get('tag'));
38855                         editor.focus();
38856                     }
38857                 }
38858
38859             });
38860             tb.addField(this.formatCombo);
38861             
38862         }
38863         
38864         if(!this.disable.format){
38865             tb.add(
38866                 btn('bold'),
38867                 btn('italic'),
38868                 btn('underline')
38869             );
38870         };
38871         if(!this.disable.fontSize){
38872             tb.add(
38873                 '-',
38874                 
38875                 
38876                 btn('increasefontsize', false, editor.adjustFont),
38877                 btn('decreasefontsize', false, editor.adjustFont)
38878             );
38879         };
38880         
38881         
38882         if(this.disable.colors){
38883             tb.add(
38884                 '-', {
38885                     id:editor.frameId +'-forecolor',
38886                     cls:'x-btn-icon x-edit-forecolor',
38887                     clickEvent:'mousedown',
38888                     tooltip: this.buttonTips['forecolor'] || undefined,
38889                     tabIndex:-1,
38890                     menu : new Roo.menu.ColorMenu({
38891                         allowReselect: true,
38892                         focus: Roo.emptyFn,
38893                         value:'000000',
38894                         plain:true,
38895                         selectHandler: function(cp, color){
38896                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
38897                             editor.deferFocus();
38898                         },
38899                         scope: editor,
38900                         clickEvent:'mousedown'
38901                     })
38902                 }, {
38903                     id:editor.frameId +'backcolor',
38904                     cls:'x-btn-icon x-edit-backcolor',
38905                     clickEvent:'mousedown',
38906                     tooltip: this.buttonTips['backcolor'] || undefined,
38907                     tabIndex:-1,
38908                     menu : new Roo.menu.ColorMenu({
38909                         focus: Roo.emptyFn,
38910                         value:'FFFFFF',
38911                         plain:true,
38912                         allowReselect: true,
38913                         selectHandler: function(cp, color){
38914                             if(Roo.isGecko){
38915                                 editor.execCmd('useCSS', false);
38916                                 editor.execCmd('hilitecolor', color);
38917                                 editor.execCmd('useCSS', true);
38918                                 editor.deferFocus();
38919                             }else{
38920                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
38921                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
38922                                 editor.deferFocus();
38923                             }
38924                         },
38925                         scope:editor,
38926                         clickEvent:'mousedown'
38927                     })
38928                 }
38929             );
38930         };
38931         // now add all the items...
38932         
38933
38934         if(!this.disable.alignments){
38935             tb.add(
38936                 '-',
38937                 btn('justifyleft'),
38938                 btn('justifycenter'),
38939                 btn('justifyright')
38940             );
38941         };
38942
38943         //if(!Roo.isSafari){
38944             if(!this.disable.links){
38945                 tb.add(
38946                     '-',
38947                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
38948                 );
38949             };
38950
38951             if(!this.disable.lists){
38952                 tb.add(
38953                     '-',
38954                     btn('insertorderedlist'),
38955                     btn('insertunorderedlist')
38956                 );
38957             }
38958             if(!this.disable.sourceEdit){
38959                 tb.add(
38960                     '-',
38961                     btn('sourceedit', true, function(btn){
38962                         this.toggleSourceEdit(btn.pressed);
38963                     })
38964                 );
38965             }
38966         //}
38967         
38968         var smenu = { };
38969         // special menu.. - needs to be tidied up..
38970         if (!this.disable.special) {
38971             smenu = {
38972                 text: "&#169;",
38973                 cls: 'x-edit-none',
38974                 menu : {
38975                     items : []
38976                    }
38977             };
38978             for (var i =0; i < this.specialChars.length; i++) {
38979                 smenu.menu.items.push({
38980                     
38981                     text: this.specialChars[i],
38982                     handler: function(a,b) {
38983                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
38984                     },
38985                     tabIndex:-1
38986                 });
38987             }
38988             
38989             
38990             tb.add(smenu);
38991             
38992             
38993         }
38994         if (this.btns) {
38995             for(var i =0; i< this.btns.length;i++) {
38996                 var b = this.btns[i];
38997                 b.cls =  'x-edit-none';
38998                 b.scope = editor;
38999                 tb.add(b);
39000             }
39001         
39002         }
39003         
39004         
39005         
39006         // disable everything...
39007         
39008         this.tb.items.each(function(item){
39009            if(item.id != editor.frameId+ '-sourceedit'){
39010                 item.disable();
39011             }
39012         });
39013         this.rendered = true;
39014         
39015         // the all the btns;
39016         editor.on('editorevent', this.updateToolbar, this);
39017         // other toolbars need to implement this..
39018         //editor.on('editmodechange', this.updateToolbar, this);
39019     },
39020     
39021     
39022     
39023     /**
39024      * Protected method that will not generally be called directly. It triggers
39025      * a toolbar update by reading the markup state of the current selection in the editor.
39026      */
39027     updateToolbar: function(){
39028
39029         if(!this.editor.activated){
39030             this.editor.onFirstFocus();
39031             return;
39032         }
39033
39034         var btns = this.tb.items.map, 
39035             doc = this.editor.doc,
39036             frameId = this.editor.frameId;
39037
39038         if(!this.disable.font && !Roo.isSafari){
39039             /*
39040             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39041             if(name != this.fontSelect.dom.value){
39042                 this.fontSelect.dom.value = name;
39043             }
39044             */
39045         }
39046         if(!this.disable.format){
39047             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39048             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39049             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39050         }
39051         if(!this.disable.alignments){
39052             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39053             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39054             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39055         }
39056         if(!Roo.isSafari && !this.disable.lists){
39057             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39058             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39059         }
39060         
39061         var ans = this.editor.getAllAncestors();
39062         if (this.formatCombo) {
39063             
39064             
39065             var store = this.formatCombo.store;
39066             this.formatCombo.setValue("");
39067             for (var i =0; i < ans.length;i++) {
39068                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
39069                     // select it..
39070                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39071                     break;
39072                 }
39073             }
39074         }
39075         
39076         
39077         
39078         // hides menus... - so this cant be on a menu...
39079         Roo.menu.MenuMgr.hideAll();
39080
39081         //this.editorsyncValue();
39082     },
39083    
39084     
39085     createFontOptions : function(){
39086         var buf = [], fs = this.fontFamilies, ff, lc;
39087         for(var i = 0, len = fs.length; i< len; i++){
39088             ff = fs[i];
39089             lc = ff.toLowerCase();
39090             buf.push(
39091                 '<option value="',lc,'" style="font-family:',ff,';"',
39092                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39093                     ff,
39094                 '</option>'
39095             );
39096         }
39097         return buf.join('');
39098     },
39099     
39100     toggleSourceEdit : function(sourceEditMode){
39101         if(sourceEditMode === undefined){
39102             sourceEditMode = !this.sourceEditMode;
39103         }
39104         this.sourceEditMode = sourceEditMode === true;
39105         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39106         // just toggle the button?
39107         if(btn.pressed !== this.editor.sourceEditMode){
39108             btn.toggle(this.editor.sourceEditMode);
39109             return;
39110         }
39111         
39112         if(this.sourceEditMode){
39113             this.tb.items.each(function(item){
39114                 if(item.cmd != 'sourceedit'){
39115                     item.disable();
39116                 }
39117             });
39118           
39119         }else{
39120             if(this.initialized){
39121                 this.tb.items.each(function(item){
39122                     item.enable();
39123                 });
39124             }
39125             
39126         }
39127         // tell the editor that it's been pressed..
39128         this.editor.toggleSourceEdit(sourceEditMode);
39129        
39130     },
39131      /**
39132      * Object collection of toolbar tooltips for the buttons in the editor. The key
39133      * is the command id associated with that button and the value is a valid QuickTips object.
39134      * For example:
39135 <pre><code>
39136 {
39137     bold : {
39138         title: 'Bold (Ctrl+B)',
39139         text: 'Make the selected text bold.',
39140         cls: 'x-html-editor-tip'
39141     },
39142     italic : {
39143         title: 'Italic (Ctrl+I)',
39144         text: 'Make the selected text italic.',
39145         cls: 'x-html-editor-tip'
39146     },
39147     ...
39148 </code></pre>
39149     * @type Object
39150      */
39151     buttonTips : {
39152         bold : {
39153             title: 'Bold (Ctrl+B)',
39154             text: 'Make the selected text bold.',
39155             cls: 'x-html-editor-tip'
39156         },
39157         italic : {
39158             title: 'Italic (Ctrl+I)',
39159             text: 'Make the selected text italic.',
39160             cls: 'x-html-editor-tip'
39161         },
39162         underline : {
39163             title: 'Underline (Ctrl+U)',
39164             text: 'Underline the selected text.',
39165             cls: 'x-html-editor-tip'
39166         },
39167         increasefontsize : {
39168             title: 'Grow Text',
39169             text: 'Increase the font size.',
39170             cls: 'x-html-editor-tip'
39171         },
39172         decreasefontsize : {
39173             title: 'Shrink Text',
39174             text: 'Decrease the font size.',
39175             cls: 'x-html-editor-tip'
39176         },
39177         backcolor : {
39178             title: 'Text Highlight Color',
39179             text: 'Change the background color of the selected text.',
39180             cls: 'x-html-editor-tip'
39181         },
39182         forecolor : {
39183             title: 'Font Color',
39184             text: 'Change the color of the selected text.',
39185             cls: 'x-html-editor-tip'
39186         },
39187         justifyleft : {
39188             title: 'Align Text Left',
39189             text: 'Align text to the left.',
39190             cls: 'x-html-editor-tip'
39191         },
39192         justifycenter : {
39193             title: 'Center Text',
39194             text: 'Center text in the editor.',
39195             cls: 'x-html-editor-tip'
39196         },
39197         justifyright : {
39198             title: 'Align Text Right',
39199             text: 'Align text to the right.',
39200             cls: 'x-html-editor-tip'
39201         },
39202         insertunorderedlist : {
39203             title: 'Bullet List',
39204             text: 'Start a bulleted list.',
39205             cls: 'x-html-editor-tip'
39206         },
39207         insertorderedlist : {
39208             title: 'Numbered List',
39209             text: 'Start a numbered list.',
39210             cls: 'x-html-editor-tip'
39211         },
39212         createlink : {
39213             title: 'Hyperlink',
39214             text: 'Make the selected text a hyperlink.',
39215             cls: 'x-html-editor-tip'
39216         },
39217         sourceedit : {
39218             title: 'Source Edit',
39219             text: 'Switch to source editing mode.',
39220             cls: 'x-html-editor-tip'
39221         }
39222     },
39223     // private
39224     onDestroy : function(){
39225         if(this.rendered){
39226             
39227             this.tb.items.each(function(item){
39228                 if(item.menu){
39229                     item.menu.removeAll();
39230                     if(item.menu.el){
39231                         item.menu.el.destroy();
39232                     }
39233                 }
39234                 item.destroy();
39235             });
39236              
39237         }
39238     },
39239     onFirstFocus: function() {
39240         this.tb.items.each(function(item){
39241            item.enable();
39242         });
39243     }
39244 });
39245
39246
39247
39248
39249 // <script type="text/javascript">
39250 /*
39251  * Based on
39252  * Ext JS Library 1.1.1
39253  * Copyright(c) 2006-2007, Ext JS, LLC.
39254  *  
39255  
39256  */
39257
39258  
39259 /**
39260  * @class Roo.form.HtmlEditor.ToolbarContext
39261  * Context Toolbar
39262  * 
39263  * Usage:
39264  *
39265  new Roo.form.HtmlEditor({
39266     ....
39267     toolbars : [
39268         new Roo.form.HtmlEditor.ToolbarStandard(),
39269         new Roo.form.HtmlEditor.ToolbarContext()
39270         })
39271     }
39272      
39273  * 
39274  * @config : {Object} disable List of elements to disable.. (not done yet.)
39275  * 
39276  * 
39277  */
39278
39279 Roo.form.HtmlEditor.ToolbarContext = function(config)
39280 {
39281     
39282     Roo.apply(this, config);
39283     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39284     // dont call parent... till later.
39285 }
39286 Roo.form.HtmlEditor.ToolbarContext.types = {
39287     'IMG' : {
39288         width : {
39289             title: "Width",
39290             width: 40
39291         },
39292         height:  {
39293             title: "Height",
39294             width: 40
39295         },
39296         align: {
39297             title: "Align",
39298             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39299             width : 80
39300             
39301         },
39302         border: {
39303             title: "Border",
39304             width: 40
39305         },
39306         alt: {
39307             title: "Alt",
39308             width: 120
39309         },
39310         src : {
39311             title: "Src",
39312             width: 220
39313         }
39314         
39315     },
39316     'A' : {
39317         name : {
39318             title: "Name",
39319             width: 50
39320         },
39321         href:  {
39322             title: "Href",
39323             width: 220
39324         } // border?
39325         
39326     },
39327     'TABLE' : {
39328         rows : {
39329             title: "Rows",
39330             width: 20
39331         },
39332         cols : {
39333             title: "Cols",
39334             width: 20
39335         },
39336         width : {
39337             title: "Width",
39338             width: 40
39339         },
39340         height : {
39341             title: "Height",
39342             width: 40
39343         },
39344         border : {
39345             title: "Border",
39346             width: 20
39347         }
39348     },
39349     'TD' : {
39350         width : {
39351             title: "Width",
39352             width: 40
39353         },
39354         height : {
39355             title: "Height",
39356             width: 40
39357         },   
39358         align: {
39359             title: "Align",
39360             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39361             width: 40
39362         },
39363         valign: {
39364             title: "Valign",
39365             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39366             width: 40
39367         },
39368         colspan: {
39369             title: "Colspan",
39370             width: 20
39371             
39372         }
39373     },
39374     'INPUT' : {
39375         name : {
39376             title: "name",
39377             width: 120
39378         },
39379         value : {
39380             title: "Value",
39381             width: 120
39382         },
39383         width : {
39384             title: "Width",
39385             width: 40
39386         }
39387     },
39388     'LABEL' : {
39389         'for' : {
39390             title: "For",
39391             width: 120
39392         }
39393     },
39394     'TEXTAREA' : {
39395           name : {
39396             title: "name",
39397             width: 120
39398         },
39399         rows : {
39400             title: "Rows",
39401             width: 20
39402         },
39403         cols : {
39404             title: "Cols",
39405             width: 20
39406         }
39407     },
39408     'SELECT' : {
39409         name : {
39410             title: "name",
39411             width: 120
39412         },
39413         selectoptions : {
39414             title: "Options",
39415             width: 200
39416         }
39417     },
39418     'BODY' : {
39419         title : {
39420             title: "title",
39421             width: 120,
39422             disabled : true
39423         }
39424     }
39425 };
39426
39427
39428
39429 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39430     
39431     tb: false,
39432     
39433     rendered: false,
39434     
39435     editor : false,
39436     /**
39437      * @cfg {Object} disable  List of toolbar elements to disable
39438          
39439      */
39440     disable : false,
39441     
39442     
39443     
39444     toolbars : false,
39445     
39446     init : function(editor)
39447     {
39448         this.editor = editor;
39449         
39450         
39451         var fid = editor.frameId;
39452         var etb = this;
39453         function btn(id, toggle, handler){
39454             var xid = fid + '-'+ id ;
39455             return {
39456                 id : xid,
39457                 cmd : id,
39458                 cls : 'x-btn-icon x-edit-'+id,
39459                 enableToggle:toggle !== false,
39460                 scope: editor, // was editor...
39461                 handler:handler||editor.relayBtnCmd,
39462                 clickEvent:'mousedown',
39463                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39464                 tabIndex:-1
39465             };
39466         }
39467         // create a new element.
39468         var wdiv = editor.wrap.createChild({
39469                 tag: 'div'
39470             }, editor.wrap.dom.firstChild.nextSibling, true);
39471         
39472         // can we do this more than once??
39473         
39474          // stop form submits
39475       
39476  
39477         // disable everything...
39478         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39479         this.toolbars = {};
39480            
39481         for (var i in  ty) {
39482             this.toolbars[i] = this.buildToolbar(ty[i],i);
39483         }
39484         this.tb = this.toolbars.BODY;
39485         this.tb.el.show();
39486         
39487          
39488         this.rendered = true;
39489         
39490         // the all the btns;
39491         editor.on('editorevent', this.updateToolbar, this);
39492         // other toolbars need to implement this..
39493         //editor.on('editmodechange', this.updateToolbar, this);
39494     },
39495     
39496     
39497     
39498     /**
39499      * Protected method that will not generally be called directly. It triggers
39500      * a toolbar update by reading the markup state of the current selection in the editor.
39501      */
39502     updateToolbar: function(){
39503
39504         if(!this.editor.activated){
39505             this.editor.onFirstFocus();
39506             return;
39507         }
39508
39509         
39510         var ans = this.editor.getAllAncestors();
39511         
39512         // pick
39513         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39514         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
39515         sel = sel ? sel : this.editor.doc.body;
39516         sel = sel.tagName.length ? sel : this.editor.doc.body;
39517         var tn = sel.tagName.toUpperCase();
39518         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
39519         tn = sel.tagName.toUpperCase();
39520         if (this.tb.name  == tn) {
39521             return; // no change
39522         }
39523         this.tb.el.hide();
39524         ///console.log("show: " + tn);
39525         this.tb =  this.toolbars[tn];
39526         this.tb.el.show();
39527         this.tb.fields.each(function(e) {
39528             e.setValue(sel.getAttribute(e.name));
39529         });
39530         this.tb.selectedNode = sel;
39531         
39532         
39533         Roo.menu.MenuMgr.hideAll();
39534
39535         //this.editorsyncValue();
39536     },
39537    
39538        
39539     // private
39540     onDestroy : function(){
39541         if(this.rendered){
39542             
39543             this.tb.items.each(function(item){
39544                 if(item.menu){
39545                     item.menu.removeAll();
39546                     if(item.menu.el){
39547                         item.menu.el.destroy();
39548                     }
39549                 }
39550                 item.destroy();
39551             });
39552              
39553         }
39554     },
39555     onFirstFocus: function() {
39556         // need to do this for all the toolbars..
39557         this.tb.items.each(function(item){
39558            item.enable();
39559         });
39560     },
39561     buildToolbar: function(tlist, nm)
39562     {
39563         var editor = this.editor;
39564          // create a new element.
39565         var wdiv = editor.wrap.createChild({
39566                 tag: 'div'
39567             }, editor.wrap.dom.firstChild.nextSibling, true);
39568         
39569        
39570         var tb = new Roo.Toolbar(wdiv);
39571         tb.add(nm+ ":&nbsp;");
39572         for (var i in tlist) {
39573             var item = tlist[i];
39574             tb.add(item.title + ":&nbsp;");
39575             if (item.opts) {
39576                 // fixme
39577                 
39578               
39579                 tb.addField( new Roo.form.ComboBox({
39580                     store: new Roo.data.SimpleStore({
39581                         id : 'val',
39582                         fields: ['val'],
39583                         data : item.opts // from states.js
39584                     }),
39585                     name : i,
39586                     displayField:'val',
39587                     typeAhead: false,
39588                     mode: 'local',
39589                     editable : false,
39590                     triggerAction: 'all',
39591                     emptyText:'Select',
39592                     selectOnFocus:true,
39593                     width: item.width ? item.width  : 130,
39594                     listeners : {
39595                         'select': function(c, r, i) {
39596                             tb.selectedNode.setAttribute(c.name, r.get('val'));
39597                         }
39598                     }
39599
39600                 }));
39601                 continue;
39602                     
39603                 
39604                 
39605                 
39606                 
39607                 tb.addField( new Roo.form.TextField({
39608                     name: i,
39609                     width: 100,
39610                     //allowBlank:false,
39611                     value: ''
39612                 }));
39613                 continue;
39614             }
39615             tb.addField( new Roo.form.TextField({
39616                 name: i,
39617                 width: item.width,
39618                 //allowBlank:true,
39619                 value: '',
39620                 listeners: {
39621                     'change' : function(f, nv, ov) {
39622                         tb.selectedNode.setAttribute(f.name, nv);
39623                     }
39624                 }
39625             }));
39626              
39627         }
39628         tb.el.on('click', function(e){
39629             e.preventDefault(); // what does this do?
39630         });
39631         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
39632         tb.el.hide();
39633         tb.name = nm;
39634         // dont need to disable them... as they will get hidden
39635         return tb;
39636          
39637         
39638     }
39639     
39640     
39641     
39642     
39643 });
39644
39645
39646
39647
39648
39649 /*
39650  * Based on:
39651  * Ext JS Library 1.1.1
39652  * Copyright(c) 2006-2007, Ext JS, LLC.
39653  *
39654  * Originally Released Under LGPL - original licence link has changed is not relivant.
39655  *
39656  * Fork - LGPL
39657  * <script type="text/javascript">
39658  */
39659  
39660 /**
39661  * @class Roo.form.BasicForm
39662  * @extends Roo.util.Observable
39663  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
39664  * @constructor
39665  * @param {String/HTMLElement/Roo.Element} el The form element or its id
39666  * @param {Object} config Configuration options
39667  */
39668 Roo.form.BasicForm = function(el, config){
39669     this.allItems = [];
39670     this.childForms = [];
39671     Roo.apply(this, config);
39672     /*
39673      * The Roo.form.Field items in this form.
39674      * @type MixedCollection
39675      */
39676      
39677      
39678     this.items = new Roo.util.MixedCollection(false, function(o){
39679         return o.id || (o.id = Roo.id());
39680     });
39681     this.addEvents({
39682         /**
39683          * @event beforeaction
39684          * Fires before any action is performed. Return false to cancel the action.
39685          * @param {Form} this
39686          * @param {Action} action The action to be performed
39687          */
39688         beforeaction: true,
39689         /**
39690          * @event actionfailed
39691          * Fires when an action fails.
39692          * @param {Form} this
39693          * @param {Action} action The action that failed
39694          */
39695         actionfailed : true,
39696         /**
39697          * @event actioncomplete
39698          * Fires when an action is completed.
39699          * @param {Form} this
39700          * @param {Action} action The action that completed
39701          */
39702         actioncomplete : true
39703     });
39704     if(el){
39705         this.initEl(el);
39706     }
39707     Roo.form.BasicForm.superclass.constructor.call(this);
39708 };
39709
39710 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
39711     /**
39712      * @cfg {String} method
39713      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
39714      */
39715     /**
39716      * @cfg {DataReader} reader
39717      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
39718      * This is optional as there is built-in support for processing JSON.
39719      */
39720     /**
39721      * @cfg {DataReader} errorReader
39722      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
39723      * This is completely optional as there is built-in support for processing JSON.
39724      */
39725     /**
39726      * @cfg {String} url
39727      * The URL to use for form actions if one isn't supplied in the action options.
39728      */
39729     /**
39730      * @cfg {Boolean} fileUpload
39731      * Set to true if this form is a file upload.
39732      */
39733     /**
39734      * @cfg {Object} baseParams
39735      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
39736      */
39737     /**
39738      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
39739      */
39740     timeout: 30,
39741
39742     // private
39743     activeAction : null,
39744
39745     /**
39746      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
39747      * or setValues() data instead of when the form was first created.
39748      */
39749     trackResetOnLoad : false,
39750     
39751     
39752     /**
39753      * childForms - used for multi-tab forms
39754      * @type {Array}
39755      */
39756     childForms : false,
39757     
39758     /**
39759      * allItems - full list of fields.
39760      * @type {Array}
39761      */
39762     allItems : false,
39763     
39764     /**
39765      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
39766      * element by passing it or its id or mask the form itself by passing in true.
39767      * @type Mixed
39768      */
39769     waitMsgTarget : undefined,
39770
39771     // private
39772     initEl : function(el){
39773         this.el = Roo.get(el);
39774         this.id = this.el.id || Roo.id();
39775         this.el.on('submit', this.onSubmit, this);
39776         this.el.addClass('x-form');
39777     },
39778
39779     // private
39780     onSubmit : function(e){
39781         e.stopEvent();
39782     },
39783
39784     /**
39785      * Returns true if client-side validation on the form is successful.
39786      * @return Boolean
39787      */
39788     isValid : function(){
39789         var valid = true;
39790         this.items.each(function(f){
39791            if(!f.validate()){
39792                valid = false;
39793            }
39794         });
39795         return valid;
39796     },
39797
39798     /**
39799      * Returns true if any fields in this form have changed since their original load.
39800      * @return Boolean
39801      */
39802     isDirty : function(){
39803         var dirty = false;
39804         this.items.each(function(f){
39805            if(f.isDirty()){
39806                dirty = true;
39807                return false;
39808            }
39809         });
39810         return dirty;
39811     },
39812
39813     /**
39814      * Performs a predefined action (submit or load) or custom actions you define on this form.
39815      * @param {String} actionName The name of the action type
39816      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
39817      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
39818      * accept other config options):
39819      * <pre>
39820 Property          Type             Description
39821 ----------------  ---------------  ----------------------------------------------------------------------------------
39822 url               String           The url for the action (defaults to the form's url)
39823 method            String           The form method to use (defaults to the form's method, or POST if not defined)
39824 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
39825 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
39826                                    validate the form on the client (defaults to false)
39827      * </pre>
39828      * @return {BasicForm} this
39829      */
39830     doAction : function(action, options){
39831         if(typeof action == 'string'){
39832             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
39833         }
39834         if(this.fireEvent('beforeaction', this, action) !== false){
39835             this.beforeAction(action);
39836             action.run.defer(100, action);
39837         }
39838         return this;
39839     },
39840
39841     /**
39842      * Shortcut to do a submit action.
39843      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39844      * @return {BasicForm} this
39845      */
39846     submit : function(options){
39847         this.doAction('submit', options);
39848         return this;
39849     },
39850
39851     /**
39852      * Shortcut to do a load action.
39853      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39854      * @return {BasicForm} this
39855      */
39856     load : function(options){
39857         this.doAction('load', options);
39858         return this;
39859     },
39860
39861     /**
39862      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
39863      * @param {Record} record The record to edit
39864      * @return {BasicForm} this
39865      */
39866     updateRecord : function(record){
39867         record.beginEdit();
39868         var fs = record.fields;
39869         fs.each(function(f){
39870             var field = this.findField(f.name);
39871             if(field){
39872                 record.set(f.name, field.getValue());
39873             }
39874         }, this);
39875         record.endEdit();
39876         return this;
39877     },
39878
39879     /**
39880      * Loads an Roo.data.Record into this form.
39881      * @param {Record} record The record to load
39882      * @return {BasicForm} this
39883      */
39884     loadRecord : function(record){
39885         this.setValues(record.data);
39886         return this;
39887     },
39888
39889     // private
39890     beforeAction : function(action){
39891         var o = action.options;
39892         if(o.waitMsg){
39893             if(this.waitMsgTarget === true){
39894                 this.el.mask(o.waitMsg, 'x-mask-loading');
39895             }else if(this.waitMsgTarget){
39896                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
39897                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
39898             }else{
39899                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
39900             }
39901         }
39902     },
39903
39904     // private
39905     afterAction : function(action, success){
39906         this.activeAction = null;
39907         var o = action.options;
39908         if(o.waitMsg){
39909             if(this.waitMsgTarget === true){
39910                 this.el.unmask();
39911             }else if(this.waitMsgTarget){
39912                 this.waitMsgTarget.unmask();
39913             }else{
39914                 Roo.MessageBox.updateProgress(1);
39915                 Roo.MessageBox.hide();
39916             }
39917         }
39918         if(success){
39919             if(o.reset){
39920                 this.reset();
39921             }
39922             Roo.callback(o.success, o.scope, [this, action]);
39923             this.fireEvent('actioncomplete', this, action);
39924         }else{
39925             Roo.callback(o.failure, o.scope, [this, action]);
39926             this.fireEvent('actionfailed', this, action);
39927         }
39928     },
39929
39930     /**
39931      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
39932      * @param {String} id The value to search for
39933      * @return Field
39934      */
39935     findField : function(id){
39936         var field = this.items.get(id);
39937         if(!field){
39938             this.items.each(function(f){
39939                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
39940                     field = f;
39941                     return false;
39942                 }
39943             });
39944         }
39945         return field || null;
39946     },
39947
39948     /**
39949      * Add a secondary form to this one, 
39950      * Used to provide tabbed forms. One form is primary, with hidden values 
39951      * which mirror the elements from the other forms.
39952      * 
39953      * @param {Roo.form.Form} form to add.
39954      * 
39955      */
39956     addForm : function(form){
39957        
39958         this.childForms.push(form);
39959         Roo.each(form.allItems, function (fe) {
39960             
39961             if (this.findField(fe.name)) { // already added..
39962                 return;
39963             }
39964             this.add( new Roo.form.Hidden({
39965                 name : fe.name
39966             }));
39967         }, this);
39968         
39969     },
39970     /**
39971      * Mark fields in this form invalid in bulk.
39972      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
39973      * @return {BasicForm} this
39974      */
39975     markInvalid : function(errors){
39976         if(errors instanceof Array){
39977             for(var i = 0, len = errors.length; i < len; i++){
39978                 var fieldError = errors[i];
39979                 var f = this.findField(fieldError.id);
39980                 if(f){
39981                     f.markInvalid(fieldError.msg);
39982                 }
39983             }
39984         }else{
39985             var field, id;
39986             for(id in errors){
39987                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
39988                     field.markInvalid(errors[id]);
39989                 }
39990             }
39991         }
39992         Roo.each(this.childForms || [], function (f) {
39993             f.markInvalid(errors);
39994         });
39995         
39996         return this;
39997     },
39998
39999     /**
40000      * Set values for fields in this form in bulk.
40001      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40002      * @return {BasicForm} this
40003      */
40004     setValues : function(values){
40005         if(values instanceof Array){ // array of objects
40006             for(var i = 0, len = values.length; i < len; i++){
40007                 var v = values[i];
40008                 var f = this.findField(v.id);
40009                 if(f){
40010                     f.setValue(v.value);
40011                     if(this.trackResetOnLoad){
40012                         f.originalValue = f.getValue();
40013                     }
40014                 }
40015             }
40016         }else{ // object hash
40017             var field, id;
40018             for(id in values){
40019                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40020                     
40021                     if (field.setFromData && 
40022                         field.valueField && 
40023                         field.displayField &&
40024                         // combos' with local stores can 
40025                         // be queried via setValue()
40026                         // to set their value..
40027                         (field.store && !field.store.isLocal)
40028                         ) {
40029                         // it's a combo
40030                         var sd = { };
40031                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40032                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40033                         field.setFromData(sd);
40034                         
40035                     } else {
40036                         field.setValue(values[id]);
40037                     }
40038                     
40039                     
40040                     if(this.trackResetOnLoad){
40041                         field.originalValue = field.getValue();
40042                     }
40043                 }
40044             }
40045         }
40046          
40047         Roo.each(this.childForms || [], function (f) {
40048             f.setValues(values);
40049         });
40050                 
40051         return this;
40052     },
40053
40054     /**
40055      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40056      * they are returned as an array.
40057      * @param {Boolean} asString
40058      * @return {Object}
40059      */
40060     getValues : function(asString){
40061         if (this.childForms) {
40062             // copy values from the child forms
40063             Roo.each(this.childForms, function (f) {
40064                 if (f.allFields) {
40065                     Roo.each(f.allFields, function (e) {
40066                         if (e.name && e.getValue && this.findField(e.name)) {
40067                             this.findField(e.name).setValue(e.getValue());
40068                         }
40069                     });
40070                 }
40071             }, this);
40072         }
40073         
40074         
40075         
40076         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40077         if(asString === true){
40078             return fs;
40079         }
40080         return Roo.urlDecode(fs);
40081     },
40082
40083     /**
40084      * Clears all invalid messages in this form.
40085      * @return {BasicForm} this
40086      */
40087     clearInvalid : function(){
40088         this.items.each(function(f){
40089            f.clearInvalid();
40090         });
40091         
40092         Roo.each(this.childForms || [], function (f) {
40093             f.clearInvalid();
40094         });
40095         
40096         
40097         return this;
40098     },
40099
40100     /**
40101      * Resets this form.
40102      * @return {BasicForm} this
40103      */
40104     reset : function(){
40105         this.items.each(function(f){
40106             f.reset();
40107         });
40108         
40109         Roo.each(this.childForms || [], function (f) {
40110             f.reset();
40111         });
40112        
40113         
40114         return this;
40115     },
40116
40117     /**
40118      * Add Roo.form components to this form.
40119      * @param {Field} field1
40120      * @param {Field} field2 (optional)
40121      * @param {Field} etc (optional)
40122      * @return {BasicForm} this
40123      */
40124     add : function(){
40125         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40126         return this;
40127     },
40128
40129
40130     /**
40131      * Removes a field from the items collection (does NOT remove its markup).
40132      * @param {Field} field
40133      * @return {BasicForm} this
40134      */
40135     remove : function(field){
40136         this.items.remove(field);
40137         return this;
40138     },
40139
40140     /**
40141      * Looks at the fields in this form, checks them for an id attribute,
40142      * and calls applyTo on the existing dom element with that id.
40143      * @return {BasicForm} this
40144      */
40145     render : function(){
40146         this.items.each(function(f){
40147             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40148                 f.applyTo(f.id);
40149             }
40150         });
40151         return this;
40152     },
40153
40154     /**
40155      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40156      * @param {Object} values
40157      * @return {BasicForm} this
40158      */
40159     applyToFields : function(o){
40160         this.items.each(function(f){
40161            Roo.apply(f, o);
40162         });
40163         return this;
40164     },
40165
40166     /**
40167      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40168      * @param {Object} values
40169      * @return {BasicForm} this
40170      */
40171     applyIfToFields : function(o){
40172         this.items.each(function(f){
40173            Roo.applyIf(f, o);
40174         });
40175         return this;
40176     }
40177 });
40178
40179 // back compat
40180 Roo.BasicForm = Roo.form.BasicForm;/*
40181  * Based on:
40182  * Ext JS Library 1.1.1
40183  * Copyright(c) 2006-2007, Ext JS, LLC.
40184  *
40185  * Originally Released Under LGPL - original licence link has changed is not relivant.
40186  *
40187  * Fork - LGPL
40188  * <script type="text/javascript">
40189  */
40190
40191 /**
40192  * @class Roo.form.Form
40193  * @extends Roo.form.BasicForm
40194  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40195  * @constructor
40196  * @param {Object} config Configuration options
40197  */
40198 Roo.form.Form = function(config){
40199     var xitems =  [];
40200     if (config.items) {
40201         xitems = config.items;
40202         delete config.items;
40203     }
40204    
40205     
40206     Roo.form.Form.superclass.constructor.call(this, null, config);
40207     this.url = this.url || this.action;
40208     if(!this.root){
40209         this.root = new Roo.form.Layout(Roo.applyIf({
40210             id: Roo.id()
40211         }, config));
40212     }
40213     this.active = this.root;
40214     /**
40215      * Array of all the buttons that have been added to this form via {@link addButton}
40216      * @type Array
40217      */
40218     this.buttons = [];
40219     this.allItems = [];
40220     this.addEvents({
40221         /**
40222          * @event clientvalidation
40223          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40224          * @param {Form} this
40225          * @param {Boolean} valid true if the form has passed client-side validation
40226          */
40227         clientvalidation: true,
40228         /**
40229          * @event rendered
40230          * Fires when the form is rendered
40231          * @param {Roo.form.Form} form
40232          */
40233         rendered : true
40234     });
40235     
40236     Roo.each(xitems, this.addxtype, this);
40237     
40238     
40239     
40240 };
40241
40242 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40243     /**
40244      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40245      */
40246     /**
40247      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40248      */
40249     /**
40250      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40251      */
40252     buttonAlign:'center',
40253
40254     /**
40255      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40256      */
40257     minButtonWidth:75,
40258
40259     /**
40260      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40261      * This property cascades to child containers if not set.
40262      */
40263     labelAlign:'left',
40264
40265     /**
40266      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40267      * fires a looping event with that state. This is required to bind buttons to the valid
40268      * state using the config value formBind:true on the button.
40269      */
40270     monitorValid : false,
40271
40272     /**
40273      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40274      */
40275     monitorPoll : 200,
40276
40277   
40278     /**
40279      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40280      * fields are added and the column is closed. If no fields are passed the column remains open
40281      * until end() is called.
40282      * @param {Object} config The config to pass to the column
40283      * @param {Field} field1 (optional)
40284      * @param {Field} field2 (optional)
40285      * @param {Field} etc (optional)
40286      * @return Column The column container object
40287      */
40288     column : function(c){
40289         var col = new Roo.form.Column(c);
40290         this.start(col);
40291         if(arguments.length > 1){ // duplicate code required because of Opera
40292             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40293             this.end();
40294         }
40295         return col;
40296     },
40297
40298     /**
40299      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40300      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40301      * until end() is called.
40302      * @param {Object} config The config to pass to the fieldset
40303      * @param {Field} field1 (optional)
40304      * @param {Field} field2 (optional)
40305      * @param {Field} etc (optional)
40306      * @return FieldSet The fieldset container object
40307      */
40308     fieldset : function(c){
40309         var fs = new Roo.form.FieldSet(c);
40310         this.start(fs);
40311         if(arguments.length > 1){ // duplicate code required because of Opera
40312             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40313             this.end();
40314         }
40315         return fs;
40316     },
40317
40318     /**
40319      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40320      * fields are added and the container is closed. If no fields are passed the container remains open
40321      * until end() is called.
40322      * @param {Object} config The config to pass to the Layout
40323      * @param {Field} field1 (optional)
40324      * @param {Field} field2 (optional)
40325      * @param {Field} etc (optional)
40326      * @return Layout The container object
40327      */
40328     container : function(c){
40329         var l = new Roo.form.Layout(c);
40330         this.start(l);
40331         if(arguments.length > 1){ // duplicate code required because of Opera
40332             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40333             this.end();
40334         }
40335         return l;
40336     },
40337
40338     /**
40339      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40340      * @param {Object} container A Roo.form.Layout or subclass of Layout
40341      * @return {Form} this
40342      */
40343     start : function(c){
40344         // cascade label info
40345         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40346         this.active.stack.push(c);
40347         c.ownerCt = this.active;
40348         this.active = c;
40349         return this;
40350     },
40351
40352     /**
40353      * Closes the current open container
40354      * @return {Form} this
40355      */
40356     end : function(){
40357         if(this.active == this.root){
40358             return this;
40359         }
40360         this.active = this.active.ownerCt;
40361         return this;
40362     },
40363
40364     /**
40365      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40366      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40367      * as the label of the field.
40368      * @param {Field} field1
40369      * @param {Field} field2 (optional)
40370      * @param {Field} etc. (optional)
40371      * @return {Form} this
40372      */
40373     add : function(){
40374         this.active.stack.push.apply(this.active.stack, arguments);
40375         this.allItems.push.apply(this.allItems,arguments);
40376         var r = [];
40377         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40378             if(a[i].isFormField){
40379                 r.push(a[i]);
40380             }
40381         }
40382         if(r.length > 0){
40383             Roo.form.Form.superclass.add.apply(this, r);
40384         }
40385         return this;
40386     },
40387     
40388
40389     
40390     
40391     
40392      /**
40393      * Find any element that has been added to a form, using it's ID or name
40394      * This can include framesets, columns etc. along with regular fields..
40395      * @param {String} id - id or name to find.
40396      
40397      * @return {Element} e - or false if nothing found.
40398      */
40399     findbyId : function(id)
40400     {
40401         var ret = false;
40402         if (!id) {
40403             return ret;
40404         }
40405         Ext.each(this.allItems, function(f){
40406             if (f.id == id || f.name == id ){
40407                 ret = f;
40408                 return false;
40409             }
40410         });
40411         return ret;
40412     },
40413
40414     
40415     
40416     /**
40417      * Render this form into the passed container. This should only be called once!
40418      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40419      * @return {Form} this
40420      */
40421     render : function(ct){
40422         ct = Roo.get(ct);
40423         var o = this.autoCreate || {
40424             tag: 'form',
40425             method : this.method || 'POST',
40426             id : this.id || Roo.id()
40427         };
40428         this.initEl(ct.createChild(o));
40429
40430         this.root.render(this.el);
40431
40432         this.items.each(function(f){
40433             f.render('x-form-el-'+f.id);
40434         });
40435
40436         if(this.buttons.length > 0){
40437             // tables are required to maintain order and for correct IE layout
40438             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40439                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
40440                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
40441             }}, null, true);
40442             var tr = tb.getElementsByTagName('tr')[0];
40443             for(var i = 0, len = this.buttons.length; i < len; i++) {
40444                 var b = this.buttons[i];
40445                 var td = document.createElement('td');
40446                 td.className = 'x-form-btn-td';
40447                 b.render(tr.appendChild(td));
40448             }
40449         }
40450         if(this.monitorValid){ // initialize after render
40451             this.startMonitoring();
40452         }
40453         this.fireEvent('rendered', this);
40454         return this;
40455     },
40456
40457     /**
40458      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
40459      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
40460      * object or a valid Roo.DomHelper element config
40461      * @param {Function} handler The function called when the button is clicked
40462      * @param {Object} scope (optional) The scope of the handler function
40463      * @return {Roo.Button}
40464      */
40465     addButton : function(config, handler, scope){
40466         var bc = {
40467             handler: handler,
40468             scope: scope,
40469             minWidth: this.minButtonWidth,
40470             hideParent:true
40471         };
40472         if(typeof config == "string"){
40473             bc.text = config;
40474         }else{
40475             Roo.apply(bc, config);
40476         }
40477         var btn = new Roo.Button(null, bc);
40478         this.buttons.push(btn);
40479         return btn;
40480     },
40481
40482      /**
40483      * Adds a series of form elements (using the xtype property as the factory method.
40484      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
40485      * @param {Object} config 
40486      */
40487     
40488     addxtype : function()
40489     {
40490         var ar = Array.prototype.slice.call(arguments, 0);
40491         var ret = false;
40492         for(var i = 0; i < ar.length; i++) {
40493             if (!ar[i]) {
40494                 continue; // skip -- if this happends something invalid got sent, we 
40495                 // should ignore it, as basically that interface element will not show up
40496                 // and that should be pretty obvious!!
40497             }
40498             
40499             if (Roo.form[ar[i].xtype]) {
40500                 ar[i].form = this;
40501                 var fe = Roo.factory(ar[i], Roo.form);
40502                 if (!ret) {
40503                     ret = fe;
40504                 }
40505                 fe.form = this;
40506                 if (fe.store) {
40507                     fe.store.form = this;
40508                 }
40509                 if (fe.isLayout) {  
40510                          
40511                     this.start(fe);
40512                     this.allItems.push(fe);
40513                     if (fe.items && fe.addxtype) {
40514                         fe.addxtype.apply(fe, fe.items);
40515                         delete fe.items;
40516                     }
40517                      this.end();
40518                     continue;
40519                 }
40520                 
40521                 
40522                  
40523                 this.add(fe);
40524               //  console.log('adding ' + ar[i].xtype);
40525             }
40526             if (ar[i].xtype == 'Button') {  
40527                 //console.log('adding button');
40528                 //console.log(ar[i]);
40529                 this.addButton(ar[i]);
40530                 this.allItems.push(fe);
40531                 continue;
40532             }
40533             
40534             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
40535                 alert('end is not supported on xtype any more, use items');
40536             //    this.end();
40537             //    //console.log('adding end');
40538             }
40539             
40540         }
40541         return ret;
40542     },
40543     
40544     /**
40545      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
40546      * option "monitorValid"
40547      */
40548     startMonitoring : function(){
40549         if(!this.bound){
40550             this.bound = true;
40551             Roo.TaskMgr.start({
40552                 run : this.bindHandler,
40553                 interval : this.monitorPoll || 200,
40554                 scope: this
40555             });
40556         }
40557     },
40558
40559     /**
40560      * Stops monitoring of the valid state of this form
40561      */
40562     stopMonitoring : function(){
40563         this.bound = false;
40564     },
40565
40566     // private
40567     bindHandler : function(){
40568         if(!this.bound){
40569             return false; // stops binding
40570         }
40571         var valid = true;
40572         this.items.each(function(f){
40573             if(!f.isValid(true)){
40574                 valid = false;
40575                 return false;
40576             }
40577         });
40578         for(var i = 0, len = this.buttons.length; i < len; i++){
40579             var btn = this.buttons[i];
40580             if(btn.formBind === true && btn.disabled === valid){
40581                 btn.setDisabled(!valid);
40582             }
40583         }
40584         this.fireEvent('clientvalidation', this, valid);
40585     }
40586     
40587     
40588     
40589     
40590     
40591     
40592     
40593     
40594 });
40595
40596
40597 // back compat
40598 Roo.Form = Roo.form.Form;
40599 /*
40600  * Based on:
40601  * Ext JS Library 1.1.1
40602  * Copyright(c) 2006-2007, Ext JS, LLC.
40603  *
40604  * Originally Released Under LGPL - original licence link has changed is not relivant.
40605  *
40606  * Fork - LGPL
40607  * <script type="text/javascript">
40608  */
40609  
40610  /**
40611  * @class Roo.form.Action
40612  * Internal Class used to handle form actions
40613  * @constructor
40614  * @param {Roo.form.BasicForm} el The form element or its id
40615  * @param {Object} config Configuration options
40616  */
40617  
40618  
40619 // define the action interface
40620 Roo.form.Action = function(form, options){
40621     this.form = form;
40622     this.options = options || {};
40623 };
40624 /**
40625  * Client Validation Failed
40626  * @const 
40627  */
40628 Roo.form.Action.CLIENT_INVALID = 'client';
40629 /**
40630  * Server Validation Failed
40631  * @const 
40632  */
40633  Roo.form.Action.SERVER_INVALID = 'server';
40634  /**
40635  * Connect to Server Failed
40636  * @const 
40637  */
40638 Roo.form.Action.CONNECT_FAILURE = 'connect';
40639 /**
40640  * Reading Data from Server Failed
40641  * @const 
40642  */
40643 Roo.form.Action.LOAD_FAILURE = 'load';
40644
40645 Roo.form.Action.prototype = {
40646     type : 'default',
40647     failureType : undefined,
40648     response : undefined,
40649     result : undefined,
40650
40651     // interface method
40652     run : function(options){
40653
40654     },
40655
40656     // interface method
40657     success : function(response){
40658
40659     },
40660
40661     // interface method
40662     handleResponse : function(response){
40663
40664     },
40665
40666     // default connection failure
40667     failure : function(response){
40668         this.response = response;
40669         this.failureType = Roo.form.Action.CONNECT_FAILURE;
40670         this.form.afterAction(this, false);
40671     },
40672
40673     processResponse : function(response){
40674         this.response = response;
40675         if(!response.responseText){
40676             return true;
40677         }
40678         this.result = this.handleResponse(response);
40679         return this.result;
40680     },
40681
40682     // utility functions used internally
40683     getUrl : function(appendParams){
40684         var url = this.options.url || this.form.url || this.form.el.dom.action;
40685         if(appendParams){
40686             var p = this.getParams();
40687             if(p){
40688                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
40689             }
40690         }
40691         return url;
40692     },
40693
40694     getMethod : function(){
40695         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
40696     },
40697
40698     getParams : function(){
40699         var bp = this.form.baseParams;
40700         var p = this.options.params;
40701         if(p){
40702             if(typeof p == "object"){
40703                 p = Roo.urlEncode(Roo.applyIf(p, bp));
40704             }else if(typeof p == 'string' && bp){
40705                 p += '&' + Roo.urlEncode(bp);
40706             }
40707         }else if(bp){
40708             p = Roo.urlEncode(bp);
40709         }
40710         return p;
40711     },
40712
40713     createCallback : function(){
40714         return {
40715             success: this.success,
40716             failure: this.failure,
40717             scope: this,
40718             timeout: (this.form.timeout*1000),
40719             upload: this.form.fileUpload ? this.success : undefined
40720         };
40721     }
40722 };
40723
40724 Roo.form.Action.Submit = function(form, options){
40725     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
40726 };
40727
40728 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
40729     type : 'submit',
40730
40731     run : function(){
40732         var o = this.options;
40733         var method = this.getMethod();
40734         var isPost = method == 'POST';
40735         if(o.clientValidation === false || this.form.isValid()){
40736             Roo.Ajax.request(Roo.apply(this.createCallback(), {
40737                 form:this.form.el.dom,
40738                 url:this.getUrl(!isPost),
40739                 method: method,
40740                 params:isPost ? this.getParams() : null,
40741                 isUpload: this.form.fileUpload
40742             }));
40743
40744         }else if (o.clientValidation !== false){ // client validation failed
40745             this.failureType = Roo.form.Action.CLIENT_INVALID;
40746             this.form.afterAction(this, false);
40747         }
40748     },
40749
40750     success : function(response){
40751         var result = this.processResponse(response);
40752         if(result === true || result.success){
40753             this.form.afterAction(this, true);
40754             return;
40755         }
40756         if(result.errors){
40757             this.form.markInvalid(result.errors);
40758             this.failureType = Roo.form.Action.SERVER_INVALID;
40759         }
40760         this.form.afterAction(this, false);
40761     },
40762
40763     handleResponse : function(response){
40764         if(this.form.errorReader){
40765             var rs = this.form.errorReader.read(response);
40766             var errors = [];
40767             if(rs.records){
40768                 for(var i = 0, len = rs.records.length; i < len; i++) {
40769                     var r = rs.records[i];
40770                     errors[i] = r.data;
40771                 }
40772             }
40773             if(errors.length < 1){
40774                 errors = null;
40775             }
40776             return {
40777                 success : rs.success,
40778                 errors : errors
40779             };
40780         }
40781         var ret = false;
40782         try {
40783             ret = Roo.decode(response.responseText);
40784         } catch (e) {
40785             ret = {
40786                 success: false,
40787                 errorMsg: "Failed to read server message: " + response.responseText,
40788                 errors : []
40789             };
40790         }
40791         return ret;
40792         
40793     }
40794 });
40795
40796
40797 Roo.form.Action.Load = function(form, options){
40798     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
40799     this.reader = this.form.reader;
40800 };
40801
40802 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
40803     type : 'load',
40804
40805     run : function(){
40806         Roo.Ajax.request(Roo.apply(
40807                 this.createCallback(), {
40808                     method:this.getMethod(),
40809                     url:this.getUrl(false),
40810                     params:this.getParams()
40811         }));
40812     },
40813
40814     success : function(response){
40815         var result = this.processResponse(response);
40816         if(result === true || !result.success || !result.data){
40817             this.failureType = Roo.form.Action.LOAD_FAILURE;
40818             this.form.afterAction(this, false);
40819             return;
40820         }
40821         this.form.clearInvalid();
40822         this.form.setValues(result.data);
40823         this.form.afterAction(this, true);
40824     },
40825
40826     handleResponse : function(response){
40827         if(this.form.reader){
40828             var rs = this.form.reader.read(response);
40829             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
40830             return {
40831                 success : rs.success,
40832                 data : data
40833             };
40834         }
40835         return Roo.decode(response.responseText);
40836     }
40837 });
40838
40839 Roo.form.Action.ACTION_TYPES = {
40840     'load' : Roo.form.Action.Load,
40841     'submit' : Roo.form.Action.Submit
40842 };/*
40843  * Based on:
40844  * Ext JS Library 1.1.1
40845  * Copyright(c) 2006-2007, Ext JS, LLC.
40846  *
40847  * Originally Released Under LGPL - original licence link has changed is not relivant.
40848  *
40849  * Fork - LGPL
40850  * <script type="text/javascript">
40851  */
40852  
40853 /**
40854  * @class Roo.form.Layout
40855  * @extends Roo.Component
40856  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
40857  * @constructor
40858  * @param {Object} config Configuration options
40859  */
40860 Roo.form.Layout = function(config){
40861     var xitems = [];
40862     if (config.items) {
40863         xitems = config.items;
40864         delete config.items;
40865     }
40866     Roo.form.Layout.superclass.constructor.call(this, config);
40867     this.stack = [];
40868     Roo.each(xitems, this.addxtype, this);
40869      
40870 };
40871
40872 Roo.extend(Roo.form.Layout, Roo.Component, {
40873     /**
40874      * @cfg {String/Object} autoCreate
40875      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
40876      */
40877     /**
40878      * @cfg {String/Object/Function} style
40879      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
40880      * a function which returns such a specification.
40881      */
40882     /**
40883      * @cfg {String} labelAlign
40884      * Valid values are "left," "top" and "right" (defaults to "left")
40885      */
40886     /**
40887      * @cfg {Number} labelWidth
40888      * Fixed width in pixels of all field labels (defaults to undefined)
40889      */
40890     /**
40891      * @cfg {Boolean} clear
40892      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
40893      */
40894     clear : true,
40895     /**
40896      * @cfg {String} labelSeparator
40897      * The separator to use after field labels (defaults to ':')
40898      */
40899     labelSeparator : ':',
40900     /**
40901      * @cfg {Boolean} hideLabels
40902      * True to suppress the display of field labels in this layout (defaults to false)
40903      */
40904     hideLabels : false,
40905
40906     // private
40907     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
40908     
40909     isLayout : true,
40910     
40911     // private
40912     onRender : function(ct, position){
40913         if(this.el){ // from markup
40914             this.el = Roo.get(this.el);
40915         }else {  // generate
40916             var cfg = this.getAutoCreate();
40917             this.el = ct.createChild(cfg, position);
40918         }
40919         if(this.style){
40920             this.el.applyStyles(this.style);
40921         }
40922         if(this.labelAlign){
40923             this.el.addClass('x-form-label-'+this.labelAlign);
40924         }
40925         if(this.hideLabels){
40926             this.labelStyle = "display:none";
40927             this.elementStyle = "padding-left:0;";
40928         }else{
40929             if(typeof this.labelWidth == 'number'){
40930                 this.labelStyle = "width:"+this.labelWidth+"px;";
40931                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
40932             }
40933             if(this.labelAlign == 'top'){
40934                 this.labelStyle = "width:auto;";
40935                 this.elementStyle = "padding-left:0;";
40936             }
40937         }
40938         var stack = this.stack;
40939         var slen = stack.length;
40940         if(slen > 0){
40941             if(!this.fieldTpl){
40942                 var t = new Roo.Template(
40943                     '<div class="x-form-item {5}">',
40944                         '<label for="{0}" style="{2}">{1}{4}</label>',
40945                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
40946                         '</div>',
40947                     '</div><div class="x-form-clear-left"></div>'
40948                 );
40949                 t.disableFormats = true;
40950                 t.compile();
40951                 Roo.form.Layout.prototype.fieldTpl = t;
40952             }
40953             for(var i = 0; i < slen; i++) {
40954                 if(stack[i].isFormField){
40955                     this.renderField(stack[i]);
40956                 }else{
40957                     this.renderComponent(stack[i]);
40958                 }
40959             }
40960         }
40961         if(this.clear){
40962             this.el.createChild({cls:'x-form-clear'});
40963         }
40964     },
40965
40966     // private
40967     renderField : function(f){
40968         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
40969                f.id, //0
40970                f.fieldLabel, //1
40971                f.labelStyle||this.labelStyle||'', //2
40972                this.elementStyle||'', //3
40973                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
40974                f.itemCls||this.itemCls||''  //5
40975        ], true).getPrevSibling());
40976     },
40977
40978     // private
40979     renderComponent : function(c){
40980         c.render(c.isLayout ? this.el : this.el.createChild());    
40981     },
40982     /**
40983      * Adds a object form elements (using the xtype property as the factory method.)
40984      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
40985      * @param {Object} config 
40986      */
40987     addxtype : function(o)
40988     {
40989         // create the lement.
40990         o.form = this.form;
40991         var fe = Roo.factory(o, Roo.form);
40992         this.form.allItems.push(fe);
40993         this.stack.push(fe);
40994         
40995         if (fe.isFormField) {
40996             this.form.items.add(fe);
40997         }
40998          
40999         return fe;
41000     }
41001 });
41002
41003 /**
41004  * @class Roo.form.Column
41005  * @extends Roo.form.Layout
41006  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41007  * @constructor
41008  * @param {Object} config Configuration options
41009  */
41010 Roo.form.Column = function(config){
41011     Roo.form.Column.superclass.constructor.call(this, config);
41012 };
41013
41014 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41015     /**
41016      * @cfg {Number/String} width
41017      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41018      */
41019     /**
41020      * @cfg {String/Object} autoCreate
41021      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41022      */
41023
41024     // private
41025     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41026
41027     // private
41028     onRender : function(ct, position){
41029         Roo.form.Column.superclass.onRender.call(this, ct, position);
41030         if(this.width){
41031             this.el.setWidth(this.width);
41032         }
41033     }
41034 });
41035
41036
41037 /**
41038  * @class Roo.form.Row
41039  * @extends Roo.form.Layout
41040  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41041  * @constructor
41042  * @param {Object} config Configuration options
41043  */
41044
41045  
41046 Roo.form.Row = function(config){
41047     Roo.form.Row.superclass.constructor.call(this, config);
41048 };
41049  
41050 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41051       /**
41052      * @cfg {Number/String} width
41053      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41054      */
41055     /**
41056      * @cfg {Number/String} height
41057      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41058      */
41059     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41060     
41061     padWidth : 20,
41062     // private
41063     onRender : function(ct, position){
41064         //console.log('row render');
41065         if(!this.rowTpl){
41066             var t = new Roo.Template(
41067                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41068                     '<label for="{0}" style="{2}">{1}{4}</label>',
41069                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41070                     '</div>',
41071                 '</div>'
41072             );
41073             t.disableFormats = true;
41074             t.compile();
41075             Roo.form.Layout.prototype.rowTpl = t;
41076         }
41077         this.fieldTpl = this.rowTpl;
41078         
41079         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41080         var labelWidth = 100;
41081         
41082         if ((this.labelAlign != 'top')) {
41083             if (typeof this.labelWidth == 'number') {
41084                 labelWidth = this.labelWidth
41085             }
41086             this.padWidth =  20 + labelWidth;
41087             
41088         }
41089         
41090         Roo.form.Column.superclass.onRender.call(this, ct, position);
41091         if(this.width){
41092             this.el.setWidth(this.width);
41093         }
41094         if(this.height){
41095             this.el.setHeight(this.height);
41096         }
41097     },
41098     
41099     // private
41100     renderField : function(f){
41101         f.fieldEl = this.fieldTpl.append(this.el, [
41102                f.id, f.fieldLabel,
41103                f.labelStyle||this.labelStyle||'',
41104                this.elementStyle||'',
41105                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41106                f.itemCls||this.itemCls||'',
41107                f.width ? f.width + this.padWidth : 160 + this.padWidth
41108        ],true);
41109     }
41110 });
41111  
41112
41113 /**
41114  * @class Roo.form.FieldSet
41115  * @extends Roo.form.Layout
41116  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41117  * @constructor
41118  * @param {Object} config Configuration options
41119  */
41120 Roo.form.FieldSet = function(config){
41121     Roo.form.FieldSet.superclass.constructor.call(this, config);
41122 };
41123
41124 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41125     /**
41126      * @cfg {String} legend
41127      * The text to display as the legend for the FieldSet (defaults to '')
41128      */
41129     /**
41130      * @cfg {String/Object} autoCreate
41131      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41132      */
41133
41134     // private
41135     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41136
41137     // private
41138     onRender : function(ct, position){
41139         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41140         if(this.legend){
41141             this.setLegend(this.legend);
41142         }
41143     },
41144
41145     // private
41146     setLegend : function(text){
41147         if(this.rendered){
41148             this.el.child('legend').update(text);
41149         }
41150     }
41151 });/*
41152  * Based on:
41153  * Ext JS Library 1.1.1
41154  * Copyright(c) 2006-2007, Ext JS, LLC.
41155  *
41156  * Originally Released Under LGPL - original licence link has changed is not relivant.
41157  *
41158  * Fork - LGPL
41159  * <script type="text/javascript">
41160  */
41161 /**
41162  * @class Roo.form.VTypes
41163  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41164  * @singleton
41165  */
41166 Roo.form.VTypes = function(){
41167     // closure these in so they are only created once.
41168     var alpha = /^[a-zA-Z_]+$/;
41169     var alphanum = /^[a-zA-Z0-9_]+$/;
41170     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41171     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41172
41173     // All these messages and functions are configurable
41174     return {
41175         /**
41176          * The function used to validate email addresses
41177          * @param {String} value The email address
41178          */
41179         'email' : function(v){
41180             return email.test(v);
41181         },
41182         /**
41183          * The error text to display when the email validation function returns false
41184          * @type String
41185          */
41186         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41187         /**
41188          * The keystroke filter mask to be applied on email input
41189          * @type RegExp
41190          */
41191         'emailMask' : /[a-z0-9_\.\-@]/i,
41192
41193         /**
41194          * The function used to validate URLs
41195          * @param {String} value The URL
41196          */
41197         'url' : function(v){
41198             return url.test(v);
41199         },
41200         /**
41201          * The error text to display when the url validation function returns false
41202          * @type String
41203          */
41204         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41205         
41206         /**
41207          * The function used to validate alpha values
41208          * @param {String} value The value
41209          */
41210         'alpha' : function(v){
41211             return alpha.test(v);
41212         },
41213         /**
41214          * The error text to display when the alpha validation function returns false
41215          * @type String
41216          */
41217         'alphaText' : 'This field should only contain letters and _',
41218         /**
41219          * The keystroke filter mask to be applied on alpha input
41220          * @type RegExp
41221          */
41222         'alphaMask' : /[a-z_]/i,
41223
41224         /**
41225          * The function used to validate alphanumeric values
41226          * @param {String} value The value
41227          */
41228         'alphanum' : function(v){
41229             return alphanum.test(v);
41230         },
41231         /**
41232          * The error text to display when the alphanumeric validation function returns false
41233          * @type String
41234          */
41235         'alphanumText' : 'This field should only contain letters, numbers and _',
41236         /**
41237          * The keystroke filter mask to be applied on alphanumeric input
41238          * @type RegExp
41239          */
41240         'alphanumMask' : /[a-z0-9_]/i
41241     };
41242 }();//<script type="text/javascript">
41243
41244 /**
41245  * @class Roo.form.FCKeditor
41246  * @extends Roo.form.TextArea
41247  * Wrapper around the FCKEditor http://www.fckeditor.net
41248  * @constructor
41249  * Creates a new FCKeditor
41250  * @param {Object} config Configuration options
41251  */
41252 Roo.form.FCKeditor = function(config){
41253     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41254     this.addEvents({
41255          /**
41256          * @event editorinit
41257          * Fired when the editor is initialized - you can add extra handlers here..
41258          * @param {FCKeditor} this
41259          * @param {Object} the FCK object.
41260          */
41261         editorinit : true
41262     });
41263     
41264     
41265 };
41266 Roo.form.FCKeditor.editors = { };
41267 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41268 {
41269     //defaultAutoCreate : {
41270     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41271     //},
41272     // private
41273     /**
41274      * @cfg {Object} fck options - see fck manual for details.
41275      */
41276     fckconfig : false,
41277     
41278     /**
41279      * @cfg {Object} fck toolbar set (Basic or Default)
41280      */
41281     toolbarSet : 'Basic',
41282     /**
41283      * @cfg {Object} fck BasePath
41284      */ 
41285     basePath : '/fckeditor/',
41286     
41287     
41288     frame : false,
41289     
41290     value : '',
41291     
41292    
41293     onRender : function(ct, position)
41294     {
41295         if(!this.el){
41296             this.defaultAutoCreate = {
41297                 tag: "textarea",
41298                 style:"width:300px;height:60px;",
41299                 autocomplete: "off"
41300             };
41301         }
41302         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41303         /*
41304         if(this.grow){
41305             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41306             if(this.preventScrollbars){
41307                 this.el.setStyle("overflow", "hidden");
41308             }
41309             this.el.setHeight(this.growMin);
41310         }
41311         */
41312         //console.log('onrender' + this.getId() );
41313         Roo.form.FCKeditor.editors[this.getId()] = this;
41314          
41315
41316         this.replaceTextarea() ;
41317         
41318     },
41319     
41320     getEditor : function() {
41321         return this.fckEditor;
41322     },
41323     /**
41324      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41325      * @param {Mixed} value The value to set
41326      */
41327     
41328     
41329     setValue : function(value)
41330     {
41331         //console.log('setValue: ' + value);
41332         
41333         if(typeof(value) == 'undefined') { // not sure why this is happending...
41334             return;
41335         }
41336         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41337         
41338         //if(!this.el || !this.getEditor()) {
41339         //    this.value = value;
41340             //this.setValue.defer(100,this,[value]);    
41341         //    return;
41342         //} 
41343         
41344         if(!this.getEditor()) {
41345             return;
41346         }
41347         
41348         this.getEditor().SetData(value);
41349         
41350         //
41351
41352     },
41353
41354     /**
41355      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41356      * @return {Mixed} value The field value
41357      */
41358     getValue : function()
41359     {
41360         
41361         if (this.frame && this.frame.dom.style.display == 'none') {
41362             return Roo.form.FCKeditor.superclass.getValue.call(this);
41363         }
41364         
41365         if(!this.el || !this.getEditor()) {
41366            
41367            // this.getValue.defer(100,this); 
41368             return this.value;
41369         }
41370        
41371         
41372         var value=this.getEditor().GetData();
41373         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41374         return Roo.form.FCKeditor.superclass.getValue.call(this);
41375         
41376
41377     },
41378
41379     /**
41380      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41381      * @return {Mixed} value The field value
41382      */
41383     getRawValue : function()
41384     {
41385         if (this.frame && this.frame.dom.style.display == 'none') {
41386             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41387         }
41388         
41389         if(!this.el || !this.getEditor()) {
41390             //this.getRawValue.defer(100,this); 
41391             return this.value;
41392             return;
41393         }
41394         
41395         
41396         
41397         var value=this.getEditor().GetData();
41398         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41399         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41400          
41401     },
41402     
41403     setSize : function(w,h) {
41404         
41405         
41406         
41407         //if (this.frame && this.frame.dom.style.display == 'none') {
41408         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41409         //    return;
41410         //}
41411         //if(!this.el || !this.getEditor()) {
41412         //    this.setSize.defer(100,this, [w,h]); 
41413         //    return;
41414         //}
41415         
41416         
41417         
41418         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41419         
41420         this.frame.dom.setAttribute('width', w);
41421         this.frame.dom.setAttribute('height', h);
41422         this.frame.setSize(w,h);
41423         
41424     },
41425     
41426     toggleSourceEdit : function(value) {
41427         
41428       
41429          
41430         this.el.dom.style.display = value ? '' : 'none';
41431         this.frame.dom.style.display = value ?  'none' : '';
41432         
41433     },
41434     
41435     
41436     focus: function(tag)
41437     {
41438         if (this.frame.dom.style.display == 'none') {
41439             return Roo.form.FCKeditor.superclass.focus.call(this);
41440         }
41441         if(!this.el || !this.getEditor()) {
41442             this.focus.defer(100,this, [tag]); 
41443             return;
41444         }
41445         
41446         
41447         
41448         
41449         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
41450         this.getEditor().Focus();
41451         if (tgs.length) {
41452             if (!this.getEditor().Selection.GetSelection()) {
41453                 this.focus.defer(100,this, [tag]); 
41454                 return;
41455             }
41456             
41457             
41458             var r = this.getEditor().EditorDocument.createRange();
41459             r.setStart(tgs[0],0);
41460             r.setEnd(tgs[0],0);
41461             this.getEditor().Selection.GetSelection().removeAllRanges();
41462             this.getEditor().Selection.GetSelection().addRange(r);
41463             this.getEditor().Focus();
41464         }
41465         
41466     },
41467     
41468     
41469     
41470     replaceTextarea : function()
41471     {
41472         if ( document.getElementById( this.getId() + '___Frame' ) )
41473             return ;
41474         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
41475         //{
41476             // We must check the elements firstly using the Id and then the name.
41477         var oTextarea = document.getElementById( this.getId() );
41478         
41479         var colElementsByName = document.getElementsByName( this.getId() ) ;
41480          
41481         oTextarea.style.display = 'none' ;
41482
41483         if ( oTextarea.tabIndex ) {            
41484             this.TabIndex = oTextarea.tabIndex ;
41485         }
41486         
41487         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
41488         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
41489         this.frame = Roo.get(this.getId() + '___Frame')
41490     },
41491     
41492     _getConfigHtml : function()
41493     {
41494         var sConfig = '' ;
41495
41496         for ( var o in this.fckconfig ) {
41497             sConfig += sConfig.length > 0  ? '&amp;' : '';
41498             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
41499         }
41500
41501         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
41502     },
41503     
41504     
41505     _getIFrameHtml : function()
41506     {
41507         var sFile = 'fckeditor.html' ;
41508         /* no idea what this is about..
41509         try
41510         {
41511             if ( (/fcksource=true/i).test( window.top.location.search ) )
41512                 sFile = 'fckeditor.original.html' ;
41513         }
41514         catch (e) { 
41515         */
41516
41517         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
41518         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
41519         
41520         
41521         var html = '<iframe id="' + this.getId() +
41522             '___Frame" src="' + sLink +
41523             '" width="' + this.width +
41524             '" height="' + this.height + '"' +
41525             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
41526             ' frameborder="0" scrolling="no"></iframe>' ;
41527
41528         return html ;
41529     },
41530     
41531     _insertHtmlBefore : function( html, element )
41532     {
41533         if ( element.insertAdjacentHTML )       {
41534             // IE
41535             element.insertAdjacentHTML( 'beforeBegin', html ) ;
41536         } else { // Gecko
41537             var oRange = document.createRange() ;
41538             oRange.setStartBefore( element ) ;
41539             var oFragment = oRange.createContextualFragment( html );
41540             element.parentNode.insertBefore( oFragment, element ) ;
41541         }
41542     }
41543     
41544     
41545   
41546     
41547     
41548     
41549     
41550
41551 });
41552
41553 //Roo.reg('fckeditor', Roo.form.FCKeditor);
41554
41555 function FCKeditor_OnComplete(editorInstance){
41556     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
41557     f.fckEditor = editorInstance;
41558     //console.log("loaded");
41559     f.fireEvent('editorinit', f, editorInstance);
41560
41561   
41562
41563  
41564
41565
41566
41567
41568
41569
41570
41571
41572
41573
41574
41575
41576
41577
41578
41579 //<script type="text/javascript">
41580 /**
41581  * @class Roo.form.GridField
41582  * @extends Roo.form.Field
41583  * Embed a grid (or editable grid into a form)
41584  * STATUS ALPHA
41585  * @constructor
41586  * Creates a new GridField
41587  * @param {Object} config Configuration options
41588  */
41589 Roo.form.GridField = function(config){
41590     Roo.form.GridField.superclass.constructor.call(this, config);
41591      
41592 };
41593
41594 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
41595     /**
41596      * @cfg {Number} width  - used to restrict width of grid..
41597      */
41598     width : 100,
41599     /**
41600      * @cfg {Number} height - used to restrict height of grid..
41601      */
41602     height : 50,
41603      /**
41604      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
41605      */
41606     xgrid : false, 
41607     /**
41608      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41609      * {tag: "input", type: "checkbox", autocomplete: "off"})
41610      */
41611    // defaultAutoCreate : { tag: 'div' },
41612     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
41613     /**
41614      * @cfg {String} addTitle Text to include for adding a title.
41615      */
41616     addTitle : false,
41617     //
41618     onResize : function(){
41619         Roo.form.Field.superclass.onResize.apply(this, arguments);
41620     },
41621
41622     initEvents : function(){
41623         // Roo.form.Checkbox.superclass.initEvents.call(this);
41624         // has no events...
41625        
41626     },
41627
41628
41629     getResizeEl : function(){
41630         return this.wrap;
41631     },
41632
41633     getPositionEl : function(){
41634         return this.wrap;
41635     },
41636
41637     // private
41638     onRender : function(ct, position){
41639         
41640         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
41641         var style = this.style;
41642         delete this.style;
41643         
41644         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
41645         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
41646         this.viewEl = this.wrap.createChild({ tag: 'div' });
41647         if (style) {
41648             this.viewEl.applyStyles(style);
41649         }
41650         if (this.width) {
41651             this.viewEl.setWidth(this.width);
41652         }
41653         if (this.height) {
41654             this.viewEl.setHeight(this.height);
41655         }
41656         //if(this.inputValue !== undefined){
41657         //this.setValue(this.value);
41658         
41659         
41660         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
41661         
41662         
41663         this.grid.render();
41664         this.grid.getDataSource().on('remove', this.refreshValue, this);
41665         this.grid.getDataSource().on('update', this.refreshValue, this);
41666         this.grid.on('afteredit', this.refreshValue, this);
41667  
41668     },
41669      
41670     
41671     /**
41672      * Sets the value of the item. 
41673      * @param {String} either an object  or a string..
41674      */
41675     setValue : function(v){
41676         //this.value = v;
41677         v = v || []; // empty set..
41678         // this does not seem smart - it really only affects memoryproxy grids..
41679         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
41680             var ds = this.grid.getDataSource();
41681             // assumes a json reader..
41682             var data = {}
41683             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
41684             ds.loadData( data);
41685         }
41686         Roo.form.GridField.superclass.setValue.call(this, v);
41687         this.refreshValue();
41688         // should load data in the grid really....
41689     },
41690     
41691     // private
41692     refreshValue: function() {
41693          var val = [];
41694         this.grid.getDataSource().each(function(r) {
41695             val.push(r.data);
41696         });
41697         this.el.dom.value = Roo.encode(val);
41698     }
41699     
41700      
41701     
41702     
41703 });//<script type="text/javasscript">
41704  
41705
41706 /**
41707  * @class Roo.DDView
41708  * A DnD enabled version of Roo.View.
41709  * @param {Element/String} container The Element in which to create the View.
41710  * @param {String} tpl The template string used to create the markup for each element of the View
41711  * @param {Object} config The configuration properties. These include all the config options of
41712  * {@link Roo.View} plus some specific to this class.<br>
41713  * <p>
41714  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
41715  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
41716  * <p>
41717  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
41718 .x-view-drag-insert-above {
41719         border-top:1px dotted #3366cc;
41720 }
41721 .x-view-drag-insert-below {
41722         border-bottom:1px dotted #3366cc;
41723 }
41724 </code></pre>
41725  * 
41726  */
41727  
41728 Roo.DDView = function(container, tpl, config) {
41729     Roo.DDView.superclass.constructor.apply(this, arguments);
41730     this.getEl().setStyle("outline", "0px none");
41731     this.getEl().unselectable();
41732     if (this.dragGroup) {
41733                 this.setDraggable(this.dragGroup.split(","));
41734     }
41735     if (this.dropGroup) {
41736                 this.setDroppable(this.dropGroup.split(","));
41737     }
41738     if (this.deletable) {
41739         this.setDeletable();
41740     }
41741     this.isDirtyFlag = false;
41742         this.addEvents({
41743                 "drop" : true
41744         });
41745 };
41746
41747 Roo.extend(Roo.DDView, Roo.View, {
41748 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
41749 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
41750 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
41751 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
41752
41753         isFormField: true,
41754
41755         reset: Roo.emptyFn,
41756         
41757         clearInvalid: Roo.form.Field.prototype.clearInvalid,
41758
41759         validate: function() {
41760                 return true;
41761         },
41762         
41763         destroy: function() {
41764                 this.purgeListeners();
41765                 this.getEl.removeAllListeners();
41766                 this.getEl().remove();
41767                 if (this.dragZone) {
41768                         if (this.dragZone.destroy) {
41769                                 this.dragZone.destroy();
41770                         }
41771                 }
41772                 if (this.dropZone) {
41773                         if (this.dropZone.destroy) {
41774                                 this.dropZone.destroy();
41775                         }
41776                 }
41777         },
41778
41779 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
41780         getName: function() {
41781                 return this.name;
41782         },
41783
41784 /**     Loads the View from a JSON string representing the Records to put into the Store. */
41785         setValue: function(v) {
41786                 if (!this.store) {
41787                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
41788                 }
41789                 var data = {};
41790                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
41791                 this.store.proxy = new Roo.data.MemoryProxy(data);
41792                 this.store.load();
41793         },
41794
41795 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
41796         getValue: function() {
41797                 var result = '(';
41798                 this.store.each(function(rec) {
41799                         result += rec.id + ',';
41800                 });
41801                 return result.substr(0, result.length - 1) + ')';
41802         },
41803         
41804         getIds: function() {
41805                 var i = 0, result = new Array(this.store.getCount());
41806                 this.store.each(function(rec) {
41807                         result[i++] = rec.id;
41808                 });
41809                 return result;
41810         },
41811         
41812         isDirty: function() {
41813                 return this.isDirtyFlag;
41814         },
41815
41816 /**
41817  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
41818  *      whole Element becomes the target, and this causes the drop gesture to append.
41819  */
41820     getTargetFromEvent : function(e) {
41821                 var target = e.getTarget();
41822                 while ((target !== null) && (target.parentNode != this.el.dom)) {
41823                 target = target.parentNode;
41824                 }
41825                 if (!target) {
41826                         target = this.el.dom.lastChild || this.el.dom;
41827                 }
41828                 return target;
41829     },
41830
41831 /**
41832  *      Create the drag data which consists of an object which has the property "ddel" as
41833  *      the drag proxy element. 
41834  */
41835     getDragData : function(e) {
41836         var target = this.findItemFromChild(e.getTarget());
41837                 if(target) {
41838                         this.handleSelection(e);
41839                         var selNodes = this.getSelectedNodes();
41840             var dragData = {
41841                 source: this,
41842                 copy: this.copy || (this.allowCopy && e.ctrlKey),
41843                 nodes: selNodes,
41844                 records: []
41845                         };
41846                         var selectedIndices = this.getSelectedIndexes();
41847                         for (var i = 0; i < selectedIndices.length; i++) {
41848                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
41849                         }
41850                         if (selNodes.length == 1) {
41851                                 dragData.ddel = target.cloneNode(true); // the div element
41852                         } else {
41853                                 var div = document.createElement('div'); // create the multi element drag "ghost"
41854                                 div.className = 'multi-proxy';
41855                                 for (var i = 0, len = selNodes.length; i < len; i++) {
41856                                         div.appendChild(selNodes[i].cloneNode(true));
41857                                 }
41858                                 dragData.ddel = div;
41859                         }
41860             //console.log(dragData)
41861             //console.log(dragData.ddel.innerHTML)
41862                         return dragData;
41863                 }
41864         //console.log('nodragData')
41865                 return false;
41866     },
41867     
41868 /**     Specify to which ddGroup items in this DDView may be dragged. */
41869     setDraggable: function(ddGroup) {
41870         if (ddGroup instanceof Array) {
41871                 Roo.each(ddGroup, this.setDraggable, this);
41872                 return;
41873         }
41874         if (this.dragZone) {
41875                 this.dragZone.addToGroup(ddGroup);
41876         } else {
41877                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
41878                                 containerScroll: true,
41879                                 ddGroup: ddGroup 
41880
41881                         });
41882 //                      Draggability implies selection. DragZone's mousedown selects the element.
41883                         if (!this.multiSelect) { this.singleSelect = true; }
41884
41885 //                      Wire the DragZone's handlers up to methods in *this*
41886                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
41887                 }
41888     },
41889
41890 /**     Specify from which ddGroup this DDView accepts drops. */
41891     setDroppable: function(ddGroup) {
41892         if (ddGroup instanceof Array) {
41893                 Roo.each(ddGroup, this.setDroppable, this);
41894                 return;
41895         }
41896         if (this.dropZone) {
41897                 this.dropZone.addToGroup(ddGroup);
41898         } else {
41899                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
41900                                 containerScroll: true,
41901                                 ddGroup: ddGroup
41902                         });
41903
41904 //                      Wire the DropZone's handlers up to methods in *this*
41905                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
41906                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
41907                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
41908                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
41909                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
41910                 }
41911     },
41912
41913 /**     Decide whether to drop above or below a View node. */
41914     getDropPoint : function(e, n, dd){
41915         if (n == this.el.dom) { return "above"; }
41916                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
41917                 var c = t + (b - t) / 2;
41918                 var y = Roo.lib.Event.getPageY(e);
41919                 if(y <= c) {
41920                         return "above";
41921                 }else{
41922                         return "below";
41923                 }
41924     },
41925
41926     onNodeEnter : function(n, dd, e, data){
41927                 return false;
41928     },
41929     
41930     onNodeOver : function(n, dd, e, data){
41931                 var pt = this.getDropPoint(e, n, dd);
41932                 // set the insert point style on the target node
41933                 var dragElClass = this.dropNotAllowed;
41934                 if (pt) {
41935                         var targetElClass;
41936                         if (pt == "above"){
41937                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
41938                                 targetElClass = "x-view-drag-insert-above";
41939                         } else {
41940                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
41941                                 targetElClass = "x-view-drag-insert-below";
41942                         }
41943                         if (this.lastInsertClass != targetElClass){
41944                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
41945                                 this.lastInsertClass = targetElClass;
41946                         }
41947                 }
41948                 return dragElClass;
41949         },
41950
41951     onNodeOut : function(n, dd, e, data){
41952                 this.removeDropIndicators(n);
41953     },
41954
41955     onNodeDrop : function(n, dd, e, data){
41956         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
41957                 return false;
41958         }
41959         var pt = this.getDropPoint(e, n, dd);
41960                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
41961                 if (pt == "below") { insertAt++; }
41962                 for (var i = 0; i < data.records.length; i++) {
41963                         var r = data.records[i];
41964                         var dup = this.store.getById(r.id);
41965                         if (dup && (dd != this.dragZone)) {
41966                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
41967                         } else {
41968                                 if (data.copy) {
41969                                         this.store.insert(insertAt++, r.copy());
41970                                 } else {
41971                                         data.source.isDirtyFlag = true;
41972                                         r.store.remove(r);
41973                                         this.store.insert(insertAt++, r);
41974                                 }
41975                                 this.isDirtyFlag = true;
41976                         }
41977                 }
41978                 this.dragZone.cachedTarget = null;
41979                 return true;
41980     },
41981
41982     removeDropIndicators : function(n){
41983                 if(n){
41984                         Roo.fly(n).removeClass([
41985                                 "x-view-drag-insert-above",
41986                                 "x-view-drag-insert-below"]);
41987                         this.lastInsertClass = "_noclass";
41988                 }
41989     },
41990
41991 /**
41992  *      Utility method. Add a delete option to the DDView's context menu.
41993  *      @param {String} imageUrl The URL of the "delete" icon image.
41994  */
41995         setDeletable: function(imageUrl) {
41996                 if (!this.singleSelect && !this.multiSelect) {
41997                         this.singleSelect = true;
41998                 }
41999                 var c = this.getContextMenu();
42000                 this.contextMenu.on("itemclick", function(item) {
42001                         switch (item.id) {
42002                                 case "delete":
42003                                         this.remove(this.getSelectedIndexes());
42004                                         break;
42005                         }
42006                 }, this);
42007                 this.contextMenu.add({
42008                         icon: imageUrl,
42009                         id: "delete",
42010                         text: 'Delete'
42011                 });
42012         },
42013         
42014 /**     Return the context menu for this DDView. */
42015         getContextMenu: function() {
42016                 if (!this.contextMenu) {
42017 //                      Create the View's context menu
42018                         this.contextMenu = new Roo.menu.Menu({
42019                                 id: this.id + "-contextmenu"
42020                         });
42021                         this.el.on("contextmenu", this.showContextMenu, this);
42022                 }
42023                 return this.contextMenu;
42024         },
42025         
42026         disableContextMenu: function() {
42027                 if (this.contextMenu) {
42028                         this.el.un("contextmenu", this.showContextMenu, this);
42029                 }
42030         },
42031
42032         showContextMenu: function(e, item) {
42033         item = this.findItemFromChild(e.getTarget());
42034                 if (item) {
42035                         e.stopEvent();
42036                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42037                         this.contextMenu.showAt(e.getXY());
42038             }
42039     },
42040
42041 /**
42042  *      Remove {@link Roo.data.Record}s at the specified indices.
42043  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42044  */
42045     remove: function(selectedIndices) {
42046                 selectedIndices = [].concat(selectedIndices);
42047                 for (var i = 0; i < selectedIndices.length; i++) {
42048                         var rec = this.store.getAt(selectedIndices[i]);
42049                         this.store.remove(rec);
42050                 }
42051     },
42052
42053 /**
42054  *      Double click fires the event, but also, if this is draggable, and there is only one other
42055  *      related DropZone, it transfers the selected node.
42056  */
42057     onDblClick : function(e){
42058         var item = this.findItemFromChild(e.getTarget());
42059         if(item){
42060             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42061                 return false;
42062             }
42063             if (this.dragGroup) {
42064                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42065                     while (targets.indexOf(this.dropZone) > -1) {
42066                             targets.remove(this.dropZone);
42067                                 }
42068                     if (targets.length == 1) {
42069                                         this.dragZone.cachedTarget = null;
42070                         var el = Roo.get(targets[0].getEl());
42071                         var box = el.getBox(true);
42072                         targets[0].onNodeDrop(el.dom, {
42073                                 target: el.dom,
42074                                 xy: [box.x, box.y + box.height - 1]
42075                         }, null, this.getDragData(e));
42076                     }
42077                 }
42078         }
42079     },
42080     
42081     handleSelection: function(e) {
42082                 this.dragZone.cachedTarget = null;
42083         var item = this.findItemFromChild(e.getTarget());
42084         if (!item) {
42085                 this.clearSelections(true);
42086                 return;
42087         }
42088                 if (item && (this.multiSelect || this.singleSelect)){
42089                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42090                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42091                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42092                                 this.unselect(item);
42093                         } else {
42094                                 this.select(item, this.multiSelect && e.ctrlKey);
42095                                 this.lastSelection = item;
42096                         }
42097                 }
42098     },
42099
42100     onItemClick : function(item, index, e){
42101                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
42102                         return false;
42103                 }
42104                 return true;
42105     },
42106
42107     unselect : function(nodeInfo, suppressEvent){
42108                 var node = this.getNode(nodeInfo);
42109                 if(node && this.isSelected(node)){
42110                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
42111                                 Roo.fly(node).removeClass(this.selectedClass);
42112                                 this.selections.remove(node);
42113                                 if(!suppressEvent){
42114                                         this.fireEvent("selectionchange", this, this.selections);
42115                                 }
42116                         }
42117                 }
42118     }
42119 });
42120 /*
42121  * Based on:
42122  * Ext JS Library 1.1.1
42123  * Copyright(c) 2006-2007, Ext JS, LLC.
42124  *
42125  * Originally Released Under LGPL - original licence link has changed is not relivant.
42126  *
42127  * Fork - LGPL
42128  * <script type="text/javascript">
42129  */
42130  
42131 /**
42132  * @class Roo.LayoutManager
42133  * @extends Roo.util.Observable
42134  * Base class for layout managers.
42135  */
42136 Roo.LayoutManager = function(container, config){
42137     Roo.LayoutManager.superclass.constructor.call(this);
42138     this.el = Roo.get(container);
42139     // ie scrollbar fix
42140     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42141         document.body.scroll = "no";
42142     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42143         this.el.position('relative');
42144     }
42145     this.id = this.el.id;
42146     this.el.addClass("x-layout-container");
42147     /** false to disable window resize monitoring @type Boolean */
42148     this.monitorWindowResize = true;
42149     this.regions = {};
42150     this.addEvents({
42151         /**
42152          * @event layout
42153          * Fires when a layout is performed. 
42154          * @param {Roo.LayoutManager} this
42155          */
42156         "layout" : true,
42157         /**
42158          * @event regionresized
42159          * Fires when the user resizes a region. 
42160          * @param {Roo.LayoutRegion} region The resized region
42161          * @param {Number} newSize The new size (width for east/west, height for north/south)
42162          */
42163         "regionresized" : true,
42164         /**
42165          * @event regioncollapsed
42166          * Fires when a region is collapsed. 
42167          * @param {Roo.LayoutRegion} region The collapsed region
42168          */
42169         "regioncollapsed" : true,
42170         /**
42171          * @event regionexpanded
42172          * Fires when a region is expanded.  
42173          * @param {Roo.LayoutRegion} region The expanded region
42174          */
42175         "regionexpanded" : true
42176     });
42177     this.updating = false;
42178     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42179 };
42180
42181 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
42182     /**
42183      * Returns true if this layout is currently being updated
42184      * @return {Boolean}
42185      */
42186     isUpdating : function(){
42187         return this.updating; 
42188     },
42189     
42190     /**
42191      * Suspend the LayoutManager from doing auto-layouts while
42192      * making multiple add or remove calls
42193      */
42194     beginUpdate : function(){
42195         this.updating = true;    
42196     },
42197     
42198     /**
42199      * Restore auto-layouts and optionally disable the manager from performing a layout
42200      * @param {Boolean} noLayout true to disable a layout update 
42201      */
42202     endUpdate : function(noLayout){
42203         this.updating = false;
42204         if(!noLayout){
42205             this.layout();
42206         }    
42207     },
42208     
42209     layout: function(){
42210         
42211     },
42212     
42213     onRegionResized : function(region, newSize){
42214         this.fireEvent("regionresized", region, newSize);
42215         this.layout();
42216     },
42217     
42218     onRegionCollapsed : function(region){
42219         this.fireEvent("regioncollapsed", region);
42220     },
42221     
42222     onRegionExpanded : function(region){
42223         this.fireEvent("regionexpanded", region);
42224     },
42225         
42226     /**
42227      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42228      * performs box-model adjustments.
42229      * @return {Object} The size as an object {width: (the width), height: (the height)}
42230      */
42231     getViewSize : function(){
42232         var size;
42233         if(this.el.dom != document.body){
42234             size = this.el.getSize();
42235         }else{
42236             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42237         }
42238         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42239         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42240         return size;
42241     },
42242     
42243     /**
42244      * Returns the Element this layout is bound to.
42245      * @return {Roo.Element}
42246      */
42247     getEl : function(){
42248         return this.el;
42249     },
42250     
42251     /**
42252      * Returns the specified region.
42253      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42254      * @return {Roo.LayoutRegion}
42255      */
42256     getRegion : function(target){
42257         return this.regions[target.toLowerCase()];
42258     },
42259     
42260     onWindowResize : function(){
42261         if(this.monitorWindowResize){
42262             this.layout();
42263         }
42264     }
42265 });/*
42266  * Based on:
42267  * Ext JS Library 1.1.1
42268  * Copyright(c) 2006-2007, Ext JS, LLC.
42269  *
42270  * Originally Released Under LGPL - original licence link has changed is not relivant.
42271  *
42272  * Fork - LGPL
42273  * <script type="text/javascript">
42274  */
42275 /**
42276  * @class Roo.BorderLayout
42277  * @extends Roo.LayoutManager
42278  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42279  * please see: <br><br>
42280  * <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>
42281  * <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>
42282  * Example:
42283  <pre><code>
42284  var layout = new Roo.BorderLayout(document.body, {
42285     north: {
42286         initialSize: 25,
42287         titlebar: false
42288     },
42289     west: {
42290         split:true,
42291         initialSize: 200,
42292         minSize: 175,
42293         maxSize: 400,
42294         titlebar: true,
42295         collapsible: true
42296     },
42297     east: {
42298         split:true,
42299         initialSize: 202,
42300         minSize: 175,
42301         maxSize: 400,
42302         titlebar: true,
42303         collapsible: true
42304     },
42305     south: {
42306         split:true,
42307         initialSize: 100,
42308         minSize: 100,
42309         maxSize: 200,
42310         titlebar: true,
42311         collapsible: true
42312     },
42313     center: {
42314         titlebar: true,
42315         autoScroll:true,
42316         resizeTabs: true,
42317         minTabWidth: 50,
42318         preferredTabWidth: 150
42319     }
42320 });
42321
42322 // shorthand
42323 var CP = Roo.ContentPanel;
42324
42325 layout.beginUpdate();
42326 layout.add("north", new CP("north", "North"));
42327 layout.add("south", new CP("south", {title: "South", closable: true}));
42328 layout.add("west", new CP("west", {title: "West"}));
42329 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
42330 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
42331 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
42332 layout.getRegion("center").showPanel("center1");
42333 layout.endUpdate();
42334 </code></pre>
42335
42336 <b>The container the layout is rendered into can be either the body element or any other element.
42337 If it is not the body element, the container needs to either be an absolute positioned element,
42338 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42339 the container size if it is not the body element.</b>
42340
42341 * @constructor
42342 * Create a new BorderLayout
42343 * @param {String/HTMLElement/Element} container The container this layout is bound to
42344 * @param {Object} config Configuration options
42345  */
42346 Roo.BorderLayout = function(container, config){
42347     config = config || {};
42348     Roo.BorderLayout.superclass.constructor.call(this, container, config);
42349     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
42350     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
42351         var target = this.factory.validRegions[i];
42352         if(config[target]){
42353             this.addRegion(target, config[target]);
42354         }
42355     }
42356 };
42357
42358 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
42359     /**
42360      * Creates and adds a new region if it doesn't already exist.
42361      * @param {String} target The target region key (north, south, east, west or center).
42362      * @param {Object} config The regions config object
42363      * @return {BorderLayoutRegion} The new region
42364      */
42365     addRegion : function(target, config){
42366         if(!this.regions[target]){
42367             var r = this.factory.create(target, this, config);
42368             this.bindRegion(target, r);
42369         }
42370         return this.regions[target];
42371     },
42372
42373     // private (kinda)
42374     bindRegion : function(name, r){
42375         this.regions[name] = r;
42376         r.on("visibilitychange", this.layout, this);
42377         r.on("paneladded", this.layout, this);
42378         r.on("panelremoved", this.layout, this);
42379         r.on("invalidated", this.layout, this);
42380         r.on("resized", this.onRegionResized, this);
42381         r.on("collapsed", this.onRegionCollapsed, this);
42382         r.on("expanded", this.onRegionExpanded, this);
42383     },
42384
42385     /**
42386      * Performs a layout update.
42387      */
42388     layout : function(){
42389         if(this.updating) return;
42390         var size = this.getViewSize();
42391         var w = size.width;
42392         var h = size.height;
42393         var centerW = w;
42394         var centerH = h;
42395         var centerY = 0;
42396         var centerX = 0;
42397         //var x = 0, y = 0;
42398
42399         var rs = this.regions;
42400         var north = rs["north"];
42401         var south = rs["south"]; 
42402         var west = rs["west"];
42403         var east = rs["east"];
42404         var center = rs["center"];
42405         //if(this.hideOnLayout){ // not supported anymore
42406             //c.el.setStyle("display", "none");
42407         //}
42408         if(north && north.isVisible()){
42409             var b = north.getBox();
42410             var m = north.getMargins();
42411             b.width = w - (m.left+m.right);
42412             b.x = m.left;
42413             b.y = m.top;
42414             centerY = b.height + b.y + m.bottom;
42415             centerH -= centerY;
42416             north.updateBox(this.safeBox(b));
42417         }
42418         if(south && south.isVisible()){
42419             var b = south.getBox();
42420             var m = south.getMargins();
42421             b.width = w - (m.left+m.right);
42422             b.x = m.left;
42423             var totalHeight = (b.height + m.top + m.bottom);
42424             b.y = h - totalHeight + m.top;
42425             centerH -= totalHeight;
42426             south.updateBox(this.safeBox(b));
42427         }
42428         if(west && west.isVisible()){
42429             var b = west.getBox();
42430             var m = west.getMargins();
42431             b.height = centerH - (m.top+m.bottom);
42432             b.x = m.left;
42433             b.y = centerY + m.top;
42434             var totalWidth = (b.width + m.left + m.right);
42435             centerX += totalWidth;
42436             centerW -= totalWidth;
42437             west.updateBox(this.safeBox(b));
42438         }
42439         if(east && east.isVisible()){
42440             var b = east.getBox();
42441             var m = east.getMargins();
42442             b.height = centerH - (m.top+m.bottom);
42443             var totalWidth = (b.width + m.left + m.right);
42444             b.x = w - totalWidth + m.left;
42445             b.y = centerY + m.top;
42446             centerW -= totalWidth;
42447             east.updateBox(this.safeBox(b));
42448         }
42449         if(center){
42450             var m = center.getMargins();
42451             var centerBox = {
42452                 x: centerX + m.left,
42453                 y: centerY + m.top,
42454                 width: centerW - (m.left+m.right),
42455                 height: centerH - (m.top+m.bottom)
42456             };
42457             //if(this.hideOnLayout){
42458                 //center.el.setStyle("display", "block");
42459             //}
42460             center.updateBox(this.safeBox(centerBox));
42461         }
42462         this.el.repaint();
42463         this.fireEvent("layout", this);
42464     },
42465
42466     // private
42467     safeBox : function(box){
42468         box.width = Math.max(0, box.width);
42469         box.height = Math.max(0, box.height);
42470         return box;
42471     },
42472
42473     /**
42474      * Adds a ContentPanel (or subclass) to this layout.
42475      * @param {String} target The target region key (north, south, east, west or center).
42476      * @param {Roo.ContentPanel} panel The panel to add
42477      * @return {Roo.ContentPanel} The added panel
42478      */
42479     add : function(target, panel){
42480          
42481         target = target.toLowerCase();
42482         return this.regions[target].add(panel);
42483     },
42484
42485     /**
42486      * Remove a ContentPanel (or subclass) to this layout.
42487      * @param {String} target The target region key (north, south, east, west or center).
42488      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42489      * @return {Roo.ContentPanel} The removed panel
42490      */
42491     remove : function(target, panel){
42492         target = target.toLowerCase();
42493         return this.regions[target].remove(panel);
42494     },
42495
42496     /**
42497      * Searches all regions for a panel with the specified id
42498      * @param {String} panelId
42499      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42500      */
42501     findPanel : function(panelId){
42502         var rs = this.regions;
42503         for(var target in rs){
42504             if(typeof rs[target] != "function"){
42505                 var p = rs[target].getPanel(panelId);
42506                 if(p){
42507                     return p;
42508                 }
42509             }
42510         }
42511         return null;
42512     },
42513
42514     /**
42515      * Searches all regions for a panel with the specified id and activates (shows) it.
42516      * @param {String/ContentPanel} panelId The panels id or the panel itself
42517      * @return {Roo.ContentPanel} The shown panel or null
42518      */
42519     showPanel : function(panelId) {
42520       var rs = this.regions;
42521       for(var target in rs){
42522          var r = rs[target];
42523          if(typeof r != "function"){
42524             if(r.hasPanel(panelId)){
42525                return r.showPanel(panelId);
42526             }
42527          }
42528       }
42529       return null;
42530    },
42531
42532    /**
42533      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42534      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42535      */
42536     restoreState : function(provider){
42537         if(!provider){
42538             provider = Roo.state.Manager;
42539         }
42540         var sm = new Roo.LayoutStateManager();
42541         sm.init(this, provider);
42542     },
42543
42544     /**
42545      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
42546      * object should contain properties for each region to add ContentPanels to, and each property's value should be
42547      * a valid ContentPanel config object.  Example:
42548      * <pre><code>
42549 // Create the main layout
42550 var layout = new Roo.BorderLayout('main-ct', {
42551     west: {
42552         split:true,
42553         minSize: 175,
42554         titlebar: true
42555     },
42556     center: {
42557         title:'Components'
42558     }
42559 }, 'main-ct');
42560
42561 // Create and add multiple ContentPanels at once via configs
42562 layout.batchAdd({
42563    west: {
42564        id: 'source-files',
42565        autoCreate:true,
42566        title:'Ext Source Files',
42567        autoScroll:true,
42568        fitToFrame:true
42569    },
42570    center : {
42571        el: cview,
42572        autoScroll:true,
42573        fitToFrame:true,
42574        toolbar: tb,
42575        resizeEl:'cbody'
42576    }
42577 });
42578 </code></pre>
42579      * @param {Object} regions An object containing ContentPanel configs by region name
42580      */
42581     batchAdd : function(regions){
42582         this.beginUpdate();
42583         for(var rname in regions){
42584             var lr = this.regions[rname];
42585             if(lr){
42586                 this.addTypedPanels(lr, regions[rname]);
42587             }
42588         }
42589         this.endUpdate();
42590     },
42591
42592     // private
42593     addTypedPanels : function(lr, ps){
42594         if(typeof ps == 'string'){
42595             lr.add(new Roo.ContentPanel(ps));
42596         }
42597         else if(ps instanceof Array){
42598             for(var i =0, len = ps.length; i < len; i++){
42599                 this.addTypedPanels(lr, ps[i]);
42600             }
42601         }
42602         else if(!ps.events){ // raw config?
42603             var el = ps.el;
42604             delete ps.el; // prevent conflict
42605             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
42606         }
42607         else {  // panel object assumed!
42608             lr.add(ps);
42609         }
42610     },
42611     /**
42612      * Adds a xtype elements to the layout.
42613      * <pre><code>
42614
42615 layout.addxtype({
42616        xtype : 'ContentPanel',
42617        region: 'west',
42618        items: [ .... ]
42619    }
42620 );
42621
42622 layout.addxtype({
42623         xtype : 'NestedLayoutPanel',
42624         region: 'west',
42625         layout: {
42626            center: { },
42627            west: { }   
42628         },
42629         items : [ ... list of content panels or nested layout panels.. ]
42630    }
42631 );
42632 </code></pre>
42633      * @param {Object} cfg Xtype definition of item to add.
42634      */
42635     addxtype : function(cfg)
42636     {
42637         // basically accepts a pannel...
42638         // can accept a layout region..!?!?
42639        // console.log('BorderLayout add ' + cfg.xtype)
42640         
42641         if (!cfg.xtype.match(/Panel$/)) {
42642             return false;
42643         }
42644         var ret = false;
42645         var region = cfg.region;
42646         delete cfg.region;
42647         
42648           
42649         var xitems = [];
42650         if (cfg.items) {
42651             xitems = cfg.items;
42652             delete cfg.items;
42653         }
42654         
42655         
42656         switch(cfg.xtype) 
42657         {
42658             case 'ContentPanel':  // ContentPanel (el, cfg)
42659                 if(cfg.autoCreate) {
42660                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42661                 } else {
42662                     var el = this.el.createChild();
42663                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42664                 }
42665                 
42666                 this.add(region, ret);
42667                 break;
42668             
42669             
42670             case 'TreePanel': // our new panel!
42671                 cfg.el = this.el.createChild();
42672                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42673                 this.add(region, ret);
42674                 break;
42675             
42676             case 'NestedLayoutPanel': 
42677                 // create a new Layout (which is  a Border Layout...
42678                 var el = this.el.createChild();
42679                 var clayout = cfg.layout;
42680                 delete cfg.layout;
42681                 clayout.items   = clayout.items  || [];
42682                 // replace this exitems with the clayout ones..
42683                 xitems = clayout.items;
42684                  
42685                 
42686                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42687                     cfg.background = false;
42688                 }
42689                 var layout = new Roo.BorderLayout(el, clayout);
42690                 
42691                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
42692                 //console.log('adding nested layout panel '  + cfg.toSource());
42693                 this.add(region, ret);
42694                 
42695                 break;
42696                 
42697             case 'GridPanel': 
42698             
42699                 // needs grid and region
42700                 
42701                 //var el = this.getRegion(region).el.createChild();
42702                 var el = this.el.createChild();
42703                 // create the grid first...
42704                 
42705                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
42706                 delete cfg.grid;
42707                 if (region == 'center' && this.active ) {
42708                     cfg.background = false;
42709                 }
42710                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
42711                 
42712                 this.add(region, ret);
42713                 if (cfg.background) {
42714                     ret.on('activate', function(gp) {
42715                         if (!gp.grid.rendered) {
42716                             gp.grid.render();
42717                         }
42718                     });
42719                 } else {
42720                     grid.render();
42721                 }
42722                 break;
42723            
42724                
42725                 
42726                 
42727             default: 
42728                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
42729                 return;
42730              // GridPanel (grid, cfg)
42731             
42732         }
42733         this.beginUpdate();
42734         // add children..
42735         Roo.each(xitems, function(i)  {
42736             ret.addxtype(i);
42737         });
42738         this.endUpdate();
42739         return ret;
42740         
42741     }
42742 });
42743
42744 /**
42745  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
42746  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
42747  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
42748  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
42749  * <pre><code>
42750 // shorthand
42751 var CP = Roo.ContentPanel;
42752
42753 var layout = Roo.BorderLayout.create({
42754     north: {
42755         initialSize: 25,
42756         titlebar: false,
42757         panels: [new CP("north", "North")]
42758     },
42759     west: {
42760         split:true,
42761         initialSize: 200,
42762         minSize: 175,
42763         maxSize: 400,
42764         titlebar: true,
42765         collapsible: true,
42766         panels: [new CP("west", {title: "West"})]
42767     },
42768     east: {
42769         split:true,
42770         initialSize: 202,
42771         minSize: 175,
42772         maxSize: 400,
42773         titlebar: true,
42774         collapsible: true,
42775         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
42776     },
42777     south: {
42778         split:true,
42779         initialSize: 100,
42780         minSize: 100,
42781         maxSize: 200,
42782         titlebar: true,
42783         collapsible: true,
42784         panels: [new CP("south", {title: "South", closable: true})]
42785     },
42786     center: {
42787         titlebar: true,
42788         autoScroll:true,
42789         resizeTabs: true,
42790         minTabWidth: 50,
42791         preferredTabWidth: 150,
42792         panels: [
42793             new CP("center1", {title: "Close Me", closable: true}),
42794             new CP("center2", {title: "Center Panel", closable: false})
42795         ]
42796     }
42797 }, document.body);
42798
42799 layout.getRegion("center").showPanel("center1");
42800 </code></pre>
42801  * @param config
42802  * @param targetEl
42803  */
42804 Roo.BorderLayout.create = function(config, targetEl){
42805     var layout = new Roo.BorderLayout(targetEl || document.body, config);
42806     layout.beginUpdate();
42807     var regions = Roo.BorderLayout.RegionFactory.validRegions;
42808     for(var j = 0, jlen = regions.length; j < jlen; j++){
42809         var lr = regions[j];
42810         if(layout.regions[lr] && config[lr].panels){
42811             var r = layout.regions[lr];
42812             var ps = config[lr].panels;
42813             layout.addTypedPanels(r, ps);
42814         }
42815     }
42816     layout.endUpdate();
42817     return layout;
42818 };
42819
42820 // private
42821 Roo.BorderLayout.RegionFactory = {
42822     // private
42823     validRegions : ["north","south","east","west","center"],
42824
42825     // private
42826     create : function(target, mgr, config){
42827         target = target.toLowerCase();
42828         if(config.lightweight || config.basic){
42829             return new Roo.BasicLayoutRegion(mgr, config, target);
42830         }
42831         switch(target){
42832             case "north":
42833                 return new Roo.NorthLayoutRegion(mgr, config);
42834             case "south":
42835                 return new Roo.SouthLayoutRegion(mgr, config);
42836             case "east":
42837                 return new Roo.EastLayoutRegion(mgr, config);
42838             case "west":
42839                 return new Roo.WestLayoutRegion(mgr, config);
42840             case "center":
42841                 return new Roo.CenterLayoutRegion(mgr, config);
42842         }
42843         throw 'Layout region "'+target+'" not supported.';
42844     }
42845 };/*
42846  * Based on:
42847  * Ext JS Library 1.1.1
42848  * Copyright(c) 2006-2007, Ext JS, LLC.
42849  *
42850  * Originally Released Under LGPL - original licence link has changed is not relivant.
42851  *
42852  * Fork - LGPL
42853  * <script type="text/javascript">
42854  */
42855  
42856 /**
42857  * @class Roo.BasicLayoutRegion
42858  * @extends Roo.util.Observable
42859  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42860  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42861  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42862  */
42863 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
42864     this.mgr = mgr;
42865     this.position  = pos;
42866     this.events = {
42867         /**
42868          * @scope Roo.BasicLayoutRegion
42869          */
42870         
42871         /**
42872          * @event beforeremove
42873          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42874          * @param {Roo.LayoutRegion} this
42875          * @param {Roo.ContentPanel} panel The panel
42876          * @param {Object} e The cancel event object
42877          */
42878         "beforeremove" : true,
42879         /**
42880          * @event invalidated
42881          * Fires when the layout for this region is changed.
42882          * @param {Roo.LayoutRegion} this
42883          */
42884         "invalidated" : true,
42885         /**
42886          * @event visibilitychange
42887          * Fires when this region is shown or hidden 
42888          * @param {Roo.LayoutRegion} this
42889          * @param {Boolean} visibility true or false
42890          */
42891         "visibilitychange" : true,
42892         /**
42893          * @event paneladded
42894          * Fires when a panel is added. 
42895          * @param {Roo.LayoutRegion} this
42896          * @param {Roo.ContentPanel} panel The panel
42897          */
42898         "paneladded" : true,
42899         /**
42900          * @event panelremoved
42901          * Fires when a panel is removed. 
42902          * @param {Roo.LayoutRegion} this
42903          * @param {Roo.ContentPanel} panel The panel
42904          */
42905         "panelremoved" : true,
42906         /**
42907          * @event collapsed
42908          * Fires when this region is collapsed.
42909          * @param {Roo.LayoutRegion} this
42910          */
42911         "collapsed" : true,
42912         /**
42913          * @event expanded
42914          * Fires when this region is expanded.
42915          * @param {Roo.LayoutRegion} this
42916          */
42917         "expanded" : true,
42918         /**
42919          * @event slideshow
42920          * Fires when this region is slid into view.
42921          * @param {Roo.LayoutRegion} this
42922          */
42923         "slideshow" : true,
42924         /**
42925          * @event slidehide
42926          * Fires when this region slides out of view. 
42927          * @param {Roo.LayoutRegion} this
42928          */
42929         "slidehide" : true,
42930         /**
42931          * @event panelactivated
42932          * Fires when a panel is activated. 
42933          * @param {Roo.LayoutRegion} this
42934          * @param {Roo.ContentPanel} panel The activated panel
42935          */
42936         "panelactivated" : true,
42937         /**
42938          * @event resized
42939          * Fires when the user resizes this region. 
42940          * @param {Roo.LayoutRegion} this
42941          * @param {Number} newSize The new size (width for east/west, height for north/south)
42942          */
42943         "resized" : true
42944     };
42945     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42946     this.panels = new Roo.util.MixedCollection();
42947     this.panels.getKey = this.getPanelId.createDelegate(this);
42948     this.box = null;
42949     this.activePanel = null;
42950     // ensure listeners are added...
42951     
42952     if (config.listeners || config.events) {
42953         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
42954             listeners : config.listeners || {},
42955             events : config.events || {}
42956         });
42957     }
42958     
42959     if(skipConfig !== true){
42960         this.applyConfig(config);
42961     }
42962 };
42963
42964 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
42965     getPanelId : function(p){
42966         return p.getId();
42967     },
42968     
42969     applyConfig : function(config){
42970         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42971         this.config = config;
42972         
42973     },
42974     
42975     /**
42976      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42977      * the width, for horizontal (north, south) the height.
42978      * @param {Number} newSize The new width or height
42979      */
42980     resizeTo : function(newSize){
42981         var el = this.el ? this.el :
42982                  (this.activePanel ? this.activePanel.getEl() : null);
42983         if(el){
42984             switch(this.position){
42985                 case "east":
42986                 case "west":
42987                     el.setWidth(newSize);
42988                     this.fireEvent("resized", this, newSize);
42989                 break;
42990                 case "north":
42991                 case "south":
42992                     el.setHeight(newSize);
42993                     this.fireEvent("resized", this, newSize);
42994                 break;                
42995             }
42996         }
42997     },
42998     
42999     getBox : function(){
43000         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43001     },
43002     
43003     getMargins : function(){
43004         return this.margins;
43005     },
43006     
43007     updateBox : function(box){
43008         this.box = box;
43009         var el = this.activePanel.getEl();
43010         el.dom.style.left = box.x + "px";
43011         el.dom.style.top = box.y + "px";
43012         this.activePanel.setSize(box.width, box.height);
43013     },
43014     
43015     /**
43016      * Returns the container element for this region.
43017      * @return {Roo.Element}
43018      */
43019     getEl : function(){
43020         return this.activePanel;
43021     },
43022     
43023     /**
43024      * Returns true if this region is currently visible.
43025      * @return {Boolean}
43026      */
43027     isVisible : function(){
43028         return this.activePanel ? true : false;
43029     },
43030     
43031     setActivePanel : function(panel){
43032         panel = this.getPanel(panel);
43033         if(this.activePanel && this.activePanel != panel){
43034             this.activePanel.setActiveState(false);
43035             this.activePanel.getEl().setLeftTop(-10000,-10000);
43036         }
43037         this.activePanel = panel;
43038         panel.setActiveState(true);
43039         if(this.box){
43040             panel.setSize(this.box.width, this.box.height);
43041         }
43042         this.fireEvent("panelactivated", this, panel);
43043         this.fireEvent("invalidated");
43044     },
43045     
43046     /**
43047      * Show the specified panel.
43048      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43049      * @return {Roo.ContentPanel} The shown panel or null
43050      */
43051     showPanel : function(panel){
43052         if(panel = this.getPanel(panel)){
43053             this.setActivePanel(panel);
43054         }
43055         return panel;
43056     },
43057     
43058     /**
43059      * Get the active panel for this region.
43060      * @return {Roo.ContentPanel} The active panel or null
43061      */
43062     getActivePanel : function(){
43063         return this.activePanel;
43064     },
43065     
43066     /**
43067      * Add the passed ContentPanel(s)
43068      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43069      * @return {Roo.ContentPanel} The panel added (if only one was added)
43070      */
43071     add : function(panel){
43072         if(arguments.length > 1){
43073             for(var i = 0, len = arguments.length; i < len; i++) {
43074                 this.add(arguments[i]);
43075             }
43076             return null;
43077         }
43078         if(this.hasPanel(panel)){
43079             this.showPanel(panel);
43080             return panel;
43081         }
43082         var el = panel.getEl();
43083         if(el.dom.parentNode != this.mgr.el.dom){
43084             this.mgr.el.dom.appendChild(el.dom);
43085         }
43086         if(panel.setRegion){
43087             panel.setRegion(this);
43088         }
43089         this.panels.add(panel);
43090         el.setStyle("position", "absolute");
43091         if(!panel.background){
43092             this.setActivePanel(panel);
43093             if(this.config.initialSize && this.panels.getCount()==1){
43094                 this.resizeTo(this.config.initialSize);
43095             }
43096         }
43097         this.fireEvent("paneladded", this, panel);
43098         return panel;
43099     },
43100     
43101     /**
43102      * Returns true if the panel is in this region.
43103      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43104      * @return {Boolean}
43105      */
43106     hasPanel : function(panel){
43107         if(typeof panel == "object"){ // must be panel obj
43108             panel = panel.getId();
43109         }
43110         return this.getPanel(panel) ? true : false;
43111     },
43112     
43113     /**
43114      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43115      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43116      * @param {Boolean} preservePanel Overrides the config preservePanel option
43117      * @return {Roo.ContentPanel} The panel that was removed
43118      */
43119     remove : function(panel, preservePanel){
43120         panel = this.getPanel(panel);
43121         if(!panel){
43122             return null;
43123         }
43124         var e = {};
43125         this.fireEvent("beforeremove", this, panel, e);
43126         if(e.cancel === true){
43127             return null;
43128         }
43129         var panelId = panel.getId();
43130         this.panels.removeKey(panelId);
43131         return panel;
43132     },
43133     
43134     /**
43135      * Returns the panel specified or null if it's not in this region.
43136      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43137      * @return {Roo.ContentPanel}
43138      */
43139     getPanel : function(id){
43140         if(typeof id == "object"){ // must be panel obj
43141             return id;
43142         }
43143         return this.panels.get(id);
43144     },
43145     
43146     /**
43147      * Returns this regions position (north/south/east/west/center).
43148      * @return {String} 
43149      */
43150     getPosition: function(){
43151         return this.position;    
43152     }
43153 });/*
43154  * Based on:
43155  * Ext JS Library 1.1.1
43156  * Copyright(c) 2006-2007, Ext JS, LLC.
43157  *
43158  * Originally Released Under LGPL - original licence link has changed is not relivant.
43159  *
43160  * Fork - LGPL
43161  * <script type="text/javascript">
43162  */
43163  
43164 /**
43165  * @class Roo.LayoutRegion
43166  * @extends Roo.BasicLayoutRegion
43167  * This class represents a region in a layout manager.
43168  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
43169  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
43170  * @cfg {Boolean} floatable False to disable floating (defaults to true)
43171  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43172  * @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})
43173  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
43174  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
43175  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43176  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43177  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43178  * @cfg {String} title The title for the region (overrides panel titles)
43179  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43180  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43181  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43182  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43183  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43184  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43185  * the space available, similar to FireFox 1.5 tabs (defaults to false)
43186  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43187  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43188  * @cfg {Boolean} showPin True to show a pin button
43189 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43190 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43191 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43192 * @cfg {Number} width  For East/West panels
43193 * @cfg {Number} height For North/South panels
43194 * @cfg {Boolean} split To show the splitter
43195  */
43196 Roo.LayoutRegion = function(mgr, config, pos){
43197     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
43198     var dh = Roo.DomHelper;
43199     /** This region's container element 
43200     * @type Roo.Element */
43201     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
43202     /** This region's title element 
43203     * @type Roo.Element */
43204
43205     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
43206         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43207         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
43208     ]}, true);
43209     this.titleEl.enableDisplayMode();
43210     /** This region's title text element 
43211     * @type HTMLElement */
43212     this.titleTextEl = this.titleEl.dom.firstChild;
43213     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43214     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43215     this.closeBtn.enableDisplayMode();
43216     this.closeBtn.on("click", this.closeClicked, this);
43217     this.closeBtn.hide();
43218
43219     this.createBody(config);
43220     this.visible = true;
43221     this.collapsed = false;
43222
43223     if(config.hideWhenEmpty){
43224         this.hide();
43225         this.on("paneladded", this.validateVisibility, this);
43226         this.on("panelremoved", this.validateVisibility, this);
43227     }
43228     this.applyConfig(config);
43229 };
43230
43231 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43232
43233     createBody : function(){
43234         /** This region's body element 
43235         * @type Roo.Element */
43236         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43237     },
43238
43239     applyConfig : function(c){
43240         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43241             var dh = Roo.DomHelper;
43242             if(c.titlebar !== false){
43243                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43244                 this.collapseBtn.on("click", this.collapse, this);
43245                 this.collapseBtn.enableDisplayMode();
43246
43247                 if(c.showPin === true || this.showPin){
43248                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43249                     this.stickBtn.enableDisplayMode();
43250                     this.stickBtn.on("click", this.expand, this);
43251                     this.stickBtn.hide();
43252                 }
43253             }
43254             /** This region's collapsed element
43255             * @type Roo.Element */
43256             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43257                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43258             ]}, true);
43259             if(c.floatable !== false){
43260                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43261                this.collapsedEl.on("click", this.collapseClick, this);
43262             }
43263
43264             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43265                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43266                    id: "message", unselectable: "on", style:{"float":"left"}});
43267                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43268              }
43269             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43270             this.expandBtn.on("click", this.expand, this);
43271         }
43272         if(this.collapseBtn){
43273             this.collapseBtn.setVisible(c.collapsible == true);
43274         }
43275         this.cmargins = c.cmargins || this.cmargins ||
43276                          (this.position == "west" || this.position == "east" ?
43277                              {top: 0, left: 2, right:2, bottom: 0} :
43278                              {top: 2, left: 0, right:0, bottom: 2});
43279         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43280         this.bottomTabs = c.tabPosition != "top";
43281         this.autoScroll = c.autoScroll || false;
43282         if(this.autoScroll){
43283             this.bodyEl.setStyle("overflow", "auto");
43284         }else{
43285             this.bodyEl.setStyle("overflow", "hidden");
43286         }
43287         //if(c.titlebar !== false){
43288             if((!c.titlebar && !c.title) || c.titlebar === false){
43289                 this.titleEl.hide();
43290             }else{
43291                 this.titleEl.show();
43292                 if(c.title){
43293                     this.titleTextEl.innerHTML = c.title;
43294                 }
43295             }
43296         //}
43297         this.duration = c.duration || .30;
43298         this.slideDuration = c.slideDuration || .45;
43299         this.config = c;
43300         if(c.collapsed){
43301             this.collapse(true);
43302         }
43303         if(c.hidden){
43304             this.hide();
43305         }
43306     },
43307     /**
43308      * Returns true if this region is currently visible.
43309      * @return {Boolean}
43310      */
43311     isVisible : function(){
43312         return this.visible;
43313     },
43314
43315     /**
43316      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43317      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43318      */
43319     setCollapsedTitle : function(title){
43320         title = title || "&#160;";
43321         if(this.collapsedTitleTextEl){
43322             this.collapsedTitleTextEl.innerHTML = title;
43323         }
43324     },
43325
43326     getBox : function(){
43327         var b;
43328         if(!this.collapsed){
43329             b = this.el.getBox(false, true);
43330         }else{
43331             b = this.collapsedEl.getBox(false, true);
43332         }
43333         return b;
43334     },
43335
43336     getMargins : function(){
43337         return this.collapsed ? this.cmargins : this.margins;
43338     },
43339
43340     highlight : function(){
43341         this.el.addClass("x-layout-panel-dragover");
43342     },
43343
43344     unhighlight : function(){
43345         this.el.removeClass("x-layout-panel-dragover");
43346     },
43347
43348     updateBox : function(box){
43349         this.box = box;
43350         if(!this.collapsed){
43351             this.el.dom.style.left = box.x + "px";
43352             this.el.dom.style.top = box.y + "px";
43353             this.updateBody(box.width, box.height);
43354         }else{
43355             this.collapsedEl.dom.style.left = box.x + "px";
43356             this.collapsedEl.dom.style.top = box.y + "px";
43357             this.collapsedEl.setSize(box.width, box.height);
43358         }
43359         if(this.tabs){
43360             this.tabs.autoSizeTabs();
43361         }
43362     },
43363
43364     updateBody : function(w, h){
43365         if(w !== null){
43366             this.el.setWidth(w);
43367             w -= this.el.getBorderWidth("rl");
43368             if(this.config.adjustments){
43369                 w += this.config.adjustments[0];
43370             }
43371         }
43372         if(h !== null){
43373             this.el.setHeight(h);
43374             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43375             h -= this.el.getBorderWidth("tb");
43376             if(this.config.adjustments){
43377                 h += this.config.adjustments[1];
43378             }
43379             this.bodyEl.setHeight(h);
43380             if(this.tabs){
43381                 h = this.tabs.syncHeight(h);
43382             }
43383         }
43384         if(this.panelSize){
43385             w = w !== null ? w : this.panelSize.width;
43386             h = h !== null ? h : this.panelSize.height;
43387         }
43388         if(this.activePanel){
43389             var el = this.activePanel.getEl();
43390             w = w !== null ? w : el.getWidth();
43391             h = h !== null ? h : el.getHeight();
43392             this.panelSize = {width: w, height: h};
43393             this.activePanel.setSize(w, h);
43394         }
43395         if(Roo.isIE && this.tabs){
43396             this.tabs.el.repaint();
43397         }
43398     },
43399
43400     /**
43401      * Returns the container element for this region.
43402      * @return {Roo.Element}
43403      */
43404     getEl : function(){
43405         return this.el;
43406     },
43407
43408     /**
43409      * Hides this region.
43410      */
43411     hide : function(){
43412         if(!this.collapsed){
43413             this.el.dom.style.left = "-2000px";
43414             this.el.hide();
43415         }else{
43416             this.collapsedEl.dom.style.left = "-2000px";
43417             this.collapsedEl.hide();
43418         }
43419         this.visible = false;
43420         this.fireEvent("visibilitychange", this, false);
43421     },
43422
43423     /**
43424      * Shows this region if it was previously hidden.
43425      */
43426     show : function(){
43427         if(!this.collapsed){
43428             this.el.show();
43429         }else{
43430             this.collapsedEl.show();
43431         }
43432         this.visible = true;
43433         this.fireEvent("visibilitychange", this, true);
43434     },
43435
43436     closeClicked : function(){
43437         if(this.activePanel){
43438             this.remove(this.activePanel);
43439         }
43440     },
43441
43442     collapseClick : function(e){
43443         if(this.isSlid){
43444            e.stopPropagation();
43445            this.slideIn();
43446         }else{
43447            e.stopPropagation();
43448            this.slideOut();
43449         }
43450     },
43451
43452     /**
43453      * Collapses this region.
43454      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43455      */
43456     collapse : function(skipAnim){
43457         if(this.collapsed) return;
43458         this.collapsed = true;
43459         if(this.split){
43460             this.split.el.hide();
43461         }
43462         if(this.config.animate && skipAnim !== true){
43463             this.fireEvent("invalidated", this);
43464             this.animateCollapse();
43465         }else{
43466             this.el.setLocation(-20000,-20000);
43467             this.el.hide();
43468             this.collapsedEl.show();
43469             this.fireEvent("collapsed", this);
43470             this.fireEvent("invalidated", this);
43471         }
43472     },
43473
43474     animateCollapse : function(){
43475         // overridden
43476     },
43477
43478     /**
43479      * Expands this region if it was previously collapsed.
43480      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43481      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43482      */
43483     expand : function(e, skipAnim){
43484         if(e) e.stopPropagation();
43485         if(!this.collapsed || this.el.hasActiveFx()) return;
43486         if(this.isSlid){
43487             this.afterSlideIn();
43488             skipAnim = true;
43489         }
43490         this.collapsed = false;
43491         if(this.config.animate && skipAnim !== true){
43492             this.animateExpand();
43493         }else{
43494             this.el.show();
43495             if(this.split){
43496                 this.split.el.show();
43497             }
43498             this.collapsedEl.setLocation(-2000,-2000);
43499             this.collapsedEl.hide();
43500             this.fireEvent("invalidated", this);
43501             this.fireEvent("expanded", this);
43502         }
43503     },
43504
43505     animateExpand : function(){
43506         // overridden
43507     },
43508
43509     initTabs : function(){
43510         this.bodyEl.setStyle("overflow", "hidden");
43511         var ts = new Roo.TabPanel(this.bodyEl.dom, {
43512             tabPosition: this.bottomTabs ? 'bottom' : 'top',
43513             disableTooltips: this.config.disableTabTips
43514         });
43515         if(this.config.hideTabs){
43516             ts.stripWrap.setDisplayed(false);
43517         }
43518         this.tabs = ts;
43519         ts.resizeTabs = this.config.resizeTabs === true;
43520         ts.minTabWidth = this.config.minTabWidth || 40;
43521         ts.maxTabWidth = this.config.maxTabWidth || 250;
43522         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43523         ts.monitorResize = false;
43524         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43525         ts.bodyEl.addClass('x-layout-tabs-body');
43526         this.panels.each(this.initPanelAsTab, this);
43527     },
43528
43529     initPanelAsTab : function(panel){
43530         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
43531                     this.config.closeOnTab && panel.isClosable());
43532         if(panel.tabTip !== undefined){
43533             ti.setTooltip(panel.tabTip);
43534         }
43535         ti.on("activate", function(){
43536               this.setActivePanel(panel);
43537         }, this);
43538         if(this.config.closeOnTab){
43539             ti.on("beforeclose", function(t, e){
43540                 e.cancel = true;
43541                 this.remove(panel);
43542             }, this);
43543         }
43544         return ti;
43545     },
43546
43547     updatePanelTitle : function(panel, title){
43548         if(this.activePanel == panel){
43549             this.updateTitle(title);
43550         }
43551         if(this.tabs){
43552             var ti = this.tabs.getTab(panel.getEl().id);
43553             ti.setText(title);
43554             if(panel.tabTip !== undefined){
43555                 ti.setTooltip(panel.tabTip);
43556             }
43557         }
43558     },
43559
43560     updateTitle : function(title){
43561         if(this.titleTextEl && !this.config.title){
43562             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43563         }
43564     },
43565
43566     setActivePanel : function(panel){
43567         panel = this.getPanel(panel);
43568         if(this.activePanel && this.activePanel != panel){
43569             this.activePanel.setActiveState(false);
43570         }
43571         this.activePanel = panel;
43572         panel.setActiveState(true);
43573         if(this.panelSize){
43574             panel.setSize(this.panelSize.width, this.panelSize.height);
43575         }
43576         if(this.closeBtn){
43577             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43578         }
43579         this.updateTitle(panel.getTitle());
43580         if(this.tabs){
43581             this.fireEvent("invalidated", this);
43582         }
43583         this.fireEvent("panelactivated", this, panel);
43584     },
43585
43586     /**
43587      * Shows the specified panel.
43588      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43589      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43590      */
43591     showPanel : function(panel){
43592         if(panel = this.getPanel(panel)){
43593             if(this.tabs){
43594                 var tab = this.tabs.getTab(panel.getEl().id);
43595                 if(tab.isHidden()){
43596                     this.tabs.unhideTab(tab.id);
43597                 }
43598                 tab.activate();
43599             }else{
43600                 this.setActivePanel(panel);
43601             }
43602         }
43603         return panel;
43604     },
43605
43606     /**
43607      * Get the active panel for this region.
43608      * @return {Roo.ContentPanel} The active panel or null
43609      */
43610     getActivePanel : function(){
43611         return this.activePanel;
43612     },
43613
43614     validateVisibility : function(){
43615         if(this.panels.getCount() < 1){
43616             this.updateTitle("&#160;");
43617             this.closeBtn.hide();
43618             this.hide();
43619         }else{
43620             if(!this.isVisible()){
43621                 this.show();
43622             }
43623         }
43624     },
43625
43626     /**
43627      * Adds the passed ContentPanel(s) to this region.
43628      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43629      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43630      */
43631     add : function(panel){
43632         if(arguments.length > 1){
43633             for(var i = 0, len = arguments.length; i < len; i++) {
43634                 this.add(arguments[i]);
43635             }
43636             return null;
43637         }
43638         if(this.hasPanel(panel)){
43639             this.showPanel(panel);
43640             return panel;
43641         }
43642         panel.setRegion(this);
43643         this.panels.add(panel);
43644         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43645             this.bodyEl.dom.appendChild(panel.getEl().dom);
43646             if(panel.background !== true){
43647                 this.setActivePanel(panel);
43648             }
43649             this.fireEvent("paneladded", this, panel);
43650             return panel;
43651         }
43652         if(!this.tabs){
43653             this.initTabs();
43654         }else{
43655             this.initPanelAsTab(panel);
43656         }
43657         if(panel.background !== true){
43658             this.tabs.activate(panel.getEl().id);
43659         }
43660         this.fireEvent("paneladded", this, panel);
43661         return panel;
43662     },
43663
43664     /**
43665      * Hides the tab for the specified panel.
43666      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43667      */
43668     hidePanel : function(panel){
43669         if(this.tabs && (panel = this.getPanel(panel))){
43670             this.tabs.hideTab(panel.getEl().id);
43671         }
43672     },
43673
43674     /**
43675      * Unhides the tab for a previously hidden panel.
43676      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43677      */
43678     unhidePanel : function(panel){
43679         if(this.tabs && (panel = this.getPanel(panel))){
43680             this.tabs.unhideTab(panel.getEl().id);
43681         }
43682     },
43683
43684     clearPanels : function(){
43685         while(this.panels.getCount() > 0){
43686              this.remove(this.panels.first());
43687         }
43688     },
43689
43690     /**
43691      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43692      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43693      * @param {Boolean} preservePanel Overrides the config preservePanel option
43694      * @return {Roo.ContentPanel} The panel that was removed
43695      */
43696     remove : function(panel, preservePanel){
43697         panel = this.getPanel(panel);
43698         if(!panel){
43699             return null;
43700         }
43701         var e = {};
43702         this.fireEvent("beforeremove", this, panel, e);
43703         if(e.cancel === true){
43704             return null;
43705         }
43706         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43707         var panelId = panel.getId();
43708         this.panels.removeKey(panelId);
43709         if(preservePanel){
43710             document.body.appendChild(panel.getEl().dom);
43711         }
43712         if(this.tabs){
43713             this.tabs.removeTab(panel.getEl().id);
43714         }else if (!preservePanel){
43715             this.bodyEl.dom.removeChild(panel.getEl().dom);
43716         }
43717         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43718             var p = this.panels.first();
43719             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43720             tempEl.appendChild(p.getEl().dom);
43721             this.bodyEl.update("");
43722             this.bodyEl.dom.appendChild(p.getEl().dom);
43723             tempEl = null;
43724             this.updateTitle(p.getTitle());
43725             this.tabs = null;
43726             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43727             this.setActivePanel(p);
43728         }
43729         panel.setRegion(null);
43730         if(this.activePanel == panel){
43731             this.activePanel = null;
43732         }
43733         if(this.config.autoDestroy !== false && preservePanel !== true){
43734             try{panel.destroy();}catch(e){}
43735         }
43736         this.fireEvent("panelremoved", this, panel);
43737         return panel;
43738     },
43739
43740     /**
43741      * Returns the TabPanel component used by this region
43742      * @return {Roo.TabPanel}
43743      */
43744     getTabs : function(){
43745         return this.tabs;
43746     },
43747
43748     createTool : function(parentEl, className){
43749         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
43750             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
43751         btn.addClassOnOver("x-layout-tools-button-over");
43752         return btn;
43753     }
43754 });/*
43755  * Based on:
43756  * Ext JS Library 1.1.1
43757  * Copyright(c) 2006-2007, Ext JS, LLC.
43758  *
43759  * Originally Released Under LGPL - original licence link has changed is not relivant.
43760  *
43761  * Fork - LGPL
43762  * <script type="text/javascript">
43763  */
43764  
43765
43766
43767 /**
43768  * @class Roo.SplitLayoutRegion
43769  * @extends Roo.LayoutRegion
43770  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43771  */
43772 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
43773     this.cursor = cursor;
43774     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
43775 };
43776
43777 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
43778     splitTip : "Drag to resize.",
43779     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43780     useSplitTips : false,
43781
43782     applyConfig : function(config){
43783         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
43784         if(config.split){
43785             if(!this.split){
43786                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
43787                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
43788                 /** The SplitBar for this region 
43789                 * @type Roo.SplitBar */
43790                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
43791                 this.split.on("moved", this.onSplitMove, this);
43792                 this.split.useShim = config.useShim === true;
43793                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43794                 if(this.useSplitTips){
43795                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43796                 }
43797                 if(config.collapsible){
43798                     this.split.el.on("dblclick", this.collapse,  this);
43799                 }
43800             }
43801             if(typeof config.minSize != "undefined"){
43802                 this.split.minSize = config.minSize;
43803             }
43804             if(typeof config.maxSize != "undefined"){
43805                 this.split.maxSize = config.maxSize;
43806             }
43807             if(config.hideWhenEmpty || config.hidden || config.collapsed){
43808                 this.hideSplitter();
43809             }
43810         }
43811     },
43812
43813     getHMaxSize : function(){
43814          var cmax = this.config.maxSize || 10000;
43815          var center = this.mgr.getRegion("center");
43816          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43817     },
43818
43819     getVMaxSize : function(){
43820          var cmax = this.config.maxSize || 10000;
43821          var center = this.mgr.getRegion("center");
43822          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43823     },
43824
43825     onSplitMove : function(split, newSize){
43826         this.fireEvent("resized", this, newSize);
43827     },
43828     
43829     /** 
43830      * Returns the {@link Roo.SplitBar} for this region.
43831      * @return {Roo.SplitBar}
43832      */
43833     getSplitBar : function(){
43834         return this.split;
43835     },
43836     
43837     hide : function(){
43838         this.hideSplitter();
43839         Roo.SplitLayoutRegion.superclass.hide.call(this);
43840     },
43841
43842     hideSplitter : function(){
43843         if(this.split){
43844             this.split.el.setLocation(-2000,-2000);
43845             this.split.el.hide();
43846         }
43847     },
43848
43849     show : function(){
43850         if(this.split){
43851             this.split.el.show();
43852         }
43853         Roo.SplitLayoutRegion.superclass.show.call(this);
43854     },
43855     
43856     beforeSlide: function(){
43857         if(Roo.isGecko){// firefox overflow auto bug workaround
43858             this.bodyEl.clip();
43859             if(this.tabs) this.tabs.bodyEl.clip();
43860             if(this.activePanel){
43861                 this.activePanel.getEl().clip();
43862                 
43863                 if(this.activePanel.beforeSlide){
43864                     this.activePanel.beforeSlide();
43865                 }
43866             }
43867         }
43868     },
43869     
43870     afterSlide : function(){
43871         if(Roo.isGecko){// firefox overflow auto bug workaround
43872             this.bodyEl.unclip();
43873             if(this.tabs) this.tabs.bodyEl.unclip();
43874             if(this.activePanel){
43875                 this.activePanel.getEl().unclip();
43876                 if(this.activePanel.afterSlide){
43877                     this.activePanel.afterSlide();
43878                 }
43879             }
43880         }
43881     },
43882
43883     initAutoHide : function(){
43884         if(this.autoHide !== false){
43885             if(!this.autoHideHd){
43886                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43887                 this.autoHideHd = {
43888                     "mouseout": function(e){
43889                         if(!e.within(this.el, true)){
43890                             st.delay(500);
43891                         }
43892                     },
43893                     "mouseover" : function(e){
43894                         st.cancel();
43895                     },
43896                     scope : this
43897                 };
43898             }
43899             this.el.on(this.autoHideHd);
43900         }
43901     },
43902
43903     clearAutoHide : function(){
43904         if(this.autoHide !== false){
43905             this.el.un("mouseout", this.autoHideHd.mouseout);
43906             this.el.un("mouseover", this.autoHideHd.mouseover);
43907         }
43908     },
43909
43910     clearMonitor : function(){
43911         Roo.get(document).un("click", this.slideInIf, this);
43912     },
43913
43914     // these names are backwards but not changed for compat
43915     slideOut : function(){
43916         if(this.isSlid || this.el.hasActiveFx()){
43917             return;
43918         }
43919         this.isSlid = true;
43920         if(this.collapseBtn){
43921             this.collapseBtn.hide();
43922         }
43923         this.closeBtnState = this.closeBtn.getStyle('display');
43924         this.closeBtn.hide();
43925         if(this.stickBtn){
43926             this.stickBtn.show();
43927         }
43928         this.el.show();
43929         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43930         this.beforeSlide();
43931         this.el.setStyle("z-index", 10001);
43932         this.el.slideIn(this.getSlideAnchor(), {
43933             callback: function(){
43934                 this.afterSlide();
43935                 this.initAutoHide();
43936                 Roo.get(document).on("click", this.slideInIf, this);
43937                 this.fireEvent("slideshow", this);
43938             },
43939             scope: this,
43940             block: true
43941         });
43942     },
43943
43944     afterSlideIn : function(){
43945         this.clearAutoHide();
43946         this.isSlid = false;
43947         this.clearMonitor();
43948         this.el.setStyle("z-index", "");
43949         if(this.collapseBtn){
43950             this.collapseBtn.show();
43951         }
43952         this.closeBtn.setStyle('display', this.closeBtnState);
43953         if(this.stickBtn){
43954             this.stickBtn.hide();
43955         }
43956         this.fireEvent("slidehide", this);
43957     },
43958
43959     slideIn : function(cb){
43960         if(!this.isSlid || this.el.hasActiveFx()){
43961             Roo.callback(cb);
43962             return;
43963         }
43964         this.isSlid = false;
43965         this.beforeSlide();
43966         this.el.slideOut(this.getSlideAnchor(), {
43967             callback: function(){
43968                 this.el.setLeftTop(-10000, -10000);
43969                 this.afterSlide();
43970                 this.afterSlideIn();
43971                 Roo.callback(cb);
43972             },
43973             scope: this,
43974             block: true
43975         });
43976     },
43977     
43978     slideInIf : function(e){
43979         if(!e.within(this.el)){
43980             this.slideIn();
43981         }
43982     },
43983
43984     animateCollapse : function(){
43985         this.beforeSlide();
43986         this.el.setStyle("z-index", 20000);
43987         var anchor = this.getSlideAnchor();
43988         this.el.slideOut(anchor, {
43989             callback : function(){
43990                 this.el.setStyle("z-index", "");
43991                 this.collapsedEl.slideIn(anchor, {duration:.3});
43992                 this.afterSlide();
43993                 this.el.setLocation(-10000,-10000);
43994                 this.el.hide();
43995                 this.fireEvent("collapsed", this);
43996             },
43997             scope: this,
43998             block: true
43999         });
44000     },
44001
44002     animateExpand : function(){
44003         this.beforeSlide();
44004         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44005         this.el.setStyle("z-index", 20000);
44006         this.collapsedEl.hide({
44007             duration:.1
44008         });
44009         this.el.slideIn(this.getSlideAnchor(), {
44010             callback : function(){
44011                 this.el.setStyle("z-index", "");
44012                 this.afterSlide();
44013                 if(this.split){
44014                     this.split.el.show();
44015                 }
44016                 this.fireEvent("invalidated", this);
44017                 this.fireEvent("expanded", this);
44018             },
44019             scope: this,
44020             block: true
44021         });
44022     },
44023
44024     anchors : {
44025         "west" : "left",
44026         "east" : "right",
44027         "north" : "top",
44028         "south" : "bottom"
44029     },
44030
44031     sanchors : {
44032         "west" : "l",
44033         "east" : "r",
44034         "north" : "t",
44035         "south" : "b"
44036     },
44037
44038     canchors : {
44039         "west" : "tl-tr",
44040         "east" : "tr-tl",
44041         "north" : "tl-bl",
44042         "south" : "bl-tl"
44043     },
44044
44045     getAnchor : function(){
44046         return this.anchors[this.position];
44047     },
44048
44049     getCollapseAnchor : function(){
44050         return this.canchors[this.position];
44051     },
44052
44053     getSlideAnchor : function(){
44054         return this.sanchors[this.position];
44055     },
44056
44057     getAlignAdj : function(){
44058         var cm = this.cmargins;
44059         switch(this.position){
44060             case "west":
44061                 return [0, 0];
44062             break;
44063             case "east":
44064                 return [0, 0];
44065             break;
44066             case "north":
44067                 return [0, 0];
44068             break;
44069             case "south":
44070                 return [0, 0];
44071             break;
44072         }
44073     },
44074
44075     getExpandAdj : function(){
44076         var c = this.collapsedEl, cm = this.cmargins;
44077         switch(this.position){
44078             case "west":
44079                 return [-(cm.right+c.getWidth()+cm.left), 0];
44080             break;
44081             case "east":
44082                 return [cm.right+c.getWidth()+cm.left, 0];
44083             break;
44084             case "north":
44085                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44086             break;
44087             case "south":
44088                 return [0, cm.top+cm.bottom+c.getHeight()];
44089             break;
44090         }
44091     }
44092 });/*
44093  * Based on:
44094  * Ext JS Library 1.1.1
44095  * Copyright(c) 2006-2007, Ext JS, LLC.
44096  *
44097  * Originally Released Under LGPL - original licence link has changed is not relivant.
44098  *
44099  * Fork - LGPL
44100  * <script type="text/javascript">
44101  */
44102 /*
44103  * These classes are private internal classes
44104  */
44105 Roo.CenterLayoutRegion = function(mgr, config){
44106     Roo.LayoutRegion.call(this, mgr, config, "center");
44107     this.visible = true;
44108     this.minWidth = config.minWidth || 20;
44109     this.minHeight = config.minHeight || 20;
44110 };
44111
44112 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
44113     hide : function(){
44114         // center panel can't be hidden
44115     },
44116     
44117     show : function(){
44118         // center panel can't be hidden
44119     },
44120     
44121     getMinWidth: function(){
44122         return this.minWidth;
44123     },
44124     
44125     getMinHeight: function(){
44126         return this.minHeight;
44127     }
44128 });
44129
44130
44131 Roo.NorthLayoutRegion = function(mgr, config){
44132     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
44133     if(this.split){
44134         this.split.placement = Roo.SplitBar.TOP;
44135         this.split.orientation = Roo.SplitBar.VERTICAL;
44136         this.split.el.addClass("x-layout-split-v");
44137     }
44138     var size = config.initialSize || config.height;
44139     if(typeof size != "undefined"){
44140         this.el.setHeight(size);
44141     }
44142 };
44143 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
44144     orientation: Roo.SplitBar.VERTICAL,
44145     getBox : function(){
44146         if(this.collapsed){
44147             return this.collapsedEl.getBox();
44148         }
44149         var box = this.el.getBox();
44150         if(this.split){
44151             box.height += this.split.el.getHeight();
44152         }
44153         return box;
44154     },
44155     
44156     updateBox : function(box){
44157         if(this.split && !this.collapsed){
44158             box.height -= this.split.el.getHeight();
44159             this.split.el.setLeft(box.x);
44160             this.split.el.setTop(box.y+box.height);
44161             this.split.el.setWidth(box.width);
44162         }
44163         if(this.collapsed){
44164             this.updateBody(box.width, null);
44165         }
44166         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44167     }
44168 });
44169
44170 Roo.SouthLayoutRegion = function(mgr, config){
44171     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
44172     if(this.split){
44173         this.split.placement = Roo.SplitBar.BOTTOM;
44174         this.split.orientation = Roo.SplitBar.VERTICAL;
44175         this.split.el.addClass("x-layout-split-v");
44176     }
44177     var size = config.initialSize || config.height;
44178     if(typeof size != "undefined"){
44179         this.el.setHeight(size);
44180     }
44181 };
44182 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
44183     orientation: Roo.SplitBar.VERTICAL,
44184     getBox : function(){
44185         if(this.collapsed){
44186             return this.collapsedEl.getBox();
44187         }
44188         var box = this.el.getBox();
44189         if(this.split){
44190             var sh = this.split.el.getHeight();
44191             box.height += sh;
44192             box.y -= sh;
44193         }
44194         return box;
44195     },
44196     
44197     updateBox : function(box){
44198         if(this.split && !this.collapsed){
44199             var sh = this.split.el.getHeight();
44200             box.height -= sh;
44201             box.y += sh;
44202             this.split.el.setLeft(box.x);
44203             this.split.el.setTop(box.y-sh);
44204             this.split.el.setWidth(box.width);
44205         }
44206         if(this.collapsed){
44207             this.updateBody(box.width, null);
44208         }
44209         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44210     }
44211 });
44212
44213 Roo.EastLayoutRegion = function(mgr, config){
44214     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44215     if(this.split){
44216         this.split.placement = Roo.SplitBar.RIGHT;
44217         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44218         this.split.el.addClass("x-layout-split-h");
44219     }
44220     var size = config.initialSize || config.width;
44221     if(typeof size != "undefined"){
44222         this.el.setWidth(size);
44223     }
44224 };
44225 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44226     orientation: Roo.SplitBar.HORIZONTAL,
44227     getBox : function(){
44228         if(this.collapsed){
44229             return this.collapsedEl.getBox();
44230         }
44231         var box = this.el.getBox();
44232         if(this.split){
44233             var sw = this.split.el.getWidth();
44234             box.width += sw;
44235             box.x -= sw;
44236         }
44237         return box;
44238     },
44239
44240     updateBox : function(box){
44241         if(this.split && !this.collapsed){
44242             var sw = this.split.el.getWidth();
44243             box.width -= sw;
44244             this.split.el.setLeft(box.x);
44245             this.split.el.setTop(box.y);
44246             this.split.el.setHeight(box.height);
44247             box.x += sw;
44248         }
44249         if(this.collapsed){
44250             this.updateBody(null, box.height);
44251         }
44252         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44253     }
44254 });
44255
44256 Roo.WestLayoutRegion = function(mgr, config){
44257     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44258     if(this.split){
44259         this.split.placement = Roo.SplitBar.LEFT;
44260         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44261         this.split.el.addClass("x-layout-split-h");
44262     }
44263     var size = config.initialSize || config.width;
44264     if(typeof size != "undefined"){
44265         this.el.setWidth(size);
44266     }
44267 };
44268 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44269     orientation: Roo.SplitBar.HORIZONTAL,
44270     getBox : function(){
44271         if(this.collapsed){
44272             return this.collapsedEl.getBox();
44273         }
44274         var box = this.el.getBox();
44275         if(this.split){
44276             box.width += this.split.el.getWidth();
44277         }
44278         return box;
44279     },
44280     
44281     updateBox : function(box){
44282         if(this.split && !this.collapsed){
44283             var sw = this.split.el.getWidth();
44284             box.width -= sw;
44285             this.split.el.setLeft(box.x+box.width);
44286             this.split.el.setTop(box.y);
44287             this.split.el.setHeight(box.height);
44288         }
44289         if(this.collapsed){
44290             this.updateBody(null, box.height);
44291         }
44292         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44293     }
44294 });
44295 /*
44296  * Based on:
44297  * Ext JS Library 1.1.1
44298  * Copyright(c) 2006-2007, Ext JS, LLC.
44299  *
44300  * Originally Released Under LGPL - original licence link has changed is not relivant.
44301  *
44302  * Fork - LGPL
44303  * <script type="text/javascript">
44304  */
44305  
44306  
44307 /*
44308  * Private internal class for reading and applying state
44309  */
44310 Roo.LayoutStateManager = function(layout){
44311      // default empty state
44312      this.state = {
44313         north: {},
44314         south: {},
44315         east: {},
44316         west: {}       
44317     };
44318 };
44319
44320 Roo.LayoutStateManager.prototype = {
44321     init : function(layout, provider){
44322         this.provider = provider;
44323         var state = provider.get(layout.id+"-layout-state");
44324         if(state){
44325             var wasUpdating = layout.isUpdating();
44326             if(!wasUpdating){
44327                 layout.beginUpdate();
44328             }
44329             for(var key in state){
44330                 if(typeof state[key] != "function"){
44331                     var rstate = state[key];
44332                     var r = layout.getRegion(key);
44333                     if(r && rstate){
44334                         if(rstate.size){
44335                             r.resizeTo(rstate.size);
44336                         }
44337                         if(rstate.collapsed == true){
44338                             r.collapse(true);
44339                         }else{
44340                             r.expand(null, true);
44341                         }
44342                     }
44343                 }
44344             }
44345             if(!wasUpdating){
44346                 layout.endUpdate();
44347             }
44348             this.state = state; 
44349         }
44350         this.layout = layout;
44351         layout.on("regionresized", this.onRegionResized, this);
44352         layout.on("regioncollapsed", this.onRegionCollapsed, this);
44353         layout.on("regionexpanded", this.onRegionExpanded, this);
44354     },
44355     
44356     storeState : function(){
44357         this.provider.set(this.layout.id+"-layout-state", this.state);
44358     },
44359     
44360     onRegionResized : function(region, newSize){
44361         this.state[region.getPosition()].size = newSize;
44362         this.storeState();
44363     },
44364     
44365     onRegionCollapsed : function(region){
44366         this.state[region.getPosition()].collapsed = true;
44367         this.storeState();
44368     },
44369     
44370     onRegionExpanded : function(region){
44371         this.state[region.getPosition()].collapsed = false;
44372         this.storeState();
44373     }
44374 };/*
44375  * Based on:
44376  * Ext JS Library 1.1.1
44377  * Copyright(c) 2006-2007, Ext JS, LLC.
44378  *
44379  * Originally Released Under LGPL - original licence link has changed is not relivant.
44380  *
44381  * Fork - LGPL
44382  * <script type="text/javascript">
44383  */
44384 /**
44385  * @class Roo.ContentPanel
44386  * @extends Roo.util.Observable
44387  * A basic ContentPanel element.
44388  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44389  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44390  * @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
44391  * @cfg {Boolean} closable True if the panel can be closed/removed
44392  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44393  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44394  * @cfg {Toolbar} toolbar A toolbar for this panel
44395  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44396  * @cfg {String} title The title for this panel
44397  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44398  * @cfg {String} url Calls {@link #setUrl} with this value
44399  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44400  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44401  * @constructor
44402  * Create a new ContentPanel.
44403  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
44404  * @param {String/Object} config A string to set only the title or a config object
44405  * @param {String} content (optional) Set the HTML content for this panel
44406  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
44407  */
44408 Roo.ContentPanel = function(el, config, content){
44409     
44410      
44411     /*
44412     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
44413         config = el;
44414         el = Roo.id();
44415     }
44416     if (config && config.parentLayout) { 
44417         el = config.parentLayout.el.createChild(); 
44418     }
44419     */
44420     if(el.autoCreate){ // xtype is available if this is called from factory
44421         config = el;
44422         el = Roo.id();
44423     }
44424     this.el = Roo.get(el);
44425     if(!this.el && config && config.autoCreate){
44426         if(typeof config.autoCreate == "object"){
44427             if(!config.autoCreate.id){
44428                 config.autoCreate.id = config.id||el;
44429             }
44430             this.el = Roo.DomHelper.append(document.body,
44431                         config.autoCreate, true);
44432         }else{
44433             this.el = Roo.DomHelper.append(document.body,
44434                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
44435         }
44436     }
44437     this.closable = false;
44438     this.loaded = false;
44439     this.active = false;
44440     if(typeof config == "string"){
44441         this.title = config;
44442     }else{
44443         Roo.apply(this, config);
44444     }
44445     
44446     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
44447         this.wrapEl = this.el.wrap();    
44448         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
44449         
44450     }
44451     
44452     
44453     
44454     if(this.resizeEl){
44455         this.resizeEl = Roo.get(this.resizeEl, true);
44456     }else{
44457         this.resizeEl = this.el;
44458     }
44459     this.addEvents({
44460         /**
44461          * @event activate
44462          * Fires when this panel is activated. 
44463          * @param {Roo.ContentPanel} this
44464          */
44465         "activate" : true,
44466         /**
44467          * @event deactivate
44468          * Fires when this panel is activated. 
44469          * @param {Roo.ContentPanel} this
44470          */
44471         "deactivate" : true,
44472
44473         /**
44474          * @event resize
44475          * Fires when this panel is resized if fitToFrame is true.
44476          * @param {Roo.ContentPanel} this
44477          * @param {Number} width The width after any component adjustments
44478          * @param {Number} height The height after any component adjustments
44479          */
44480         "resize" : true
44481     });
44482     if(this.autoScroll){
44483         this.resizeEl.setStyle("overflow", "auto");
44484     }
44485     content = content || this.content;
44486     if(content){
44487         this.setContent(content);
44488     }
44489     if(config && config.url){
44490         this.setUrl(this.url, this.params, this.loadOnce);
44491     }
44492     
44493     
44494     
44495     Roo.ContentPanel.superclass.constructor.call(this);
44496 };
44497
44498 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
44499     tabTip:'',
44500     setRegion : function(region){
44501         this.region = region;
44502         if(region){
44503            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
44504         }else{
44505            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
44506         } 
44507     },
44508     
44509     /**
44510      * Returns the toolbar for this Panel if one was configured. 
44511      * @return {Roo.Toolbar} 
44512      */
44513     getToolbar : function(){
44514         return this.toolbar;
44515     },
44516     
44517     setActiveState : function(active){
44518         this.active = active;
44519         if(!active){
44520             this.fireEvent("deactivate", this);
44521         }else{
44522             this.fireEvent("activate", this);
44523         }
44524     },
44525     /**
44526      * Updates this panel's element
44527      * @param {String} content The new content
44528      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44529     */
44530     setContent : function(content, loadScripts){
44531         this.el.update(content, loadScripts);
44532     },
44533
44534     ignoreResize : function(w, h){
44535         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44536             return true;
44537         }else{
44538             this.lastSize = {width: w, height: h};
44539             return false;
44540         }
44541     },
44542     /**
44543      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44544      * @return {Roo.UpdateManager} The UpdateManager
44545      */
44546     getUpdateManager : function(){
44547         return this.el.getUpdateManager();
44548     },
44549      /**
44550      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44551      * @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:
44552 <pre><code>
44553 panel.load({
44554     url: "your-url.php",
44555     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44556     callback: yourFunction,
44557     scope: yourObject, //(optional scope)
44558     discardUrl: false,
44559     nocache: false,
44560     text: "Loading...",
44561     timeout: 30,
44562     scripts: false
44563 });
44564 </code></pre>
44565      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44566      * 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.
44567      * @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}
44568      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44569      * @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.
44570      * @return {Roo.ContentPanel} this
44571      */
44572     load : function(){
44573         var um = this.el.getUpdateManager();
44574         um.update.apply(um, arguments);
44575         return this;
44576     },
44577
44578
44579     /**
44580      * 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.
44581      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44582      * @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)
44583      * @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)
44584      * @return {Roo.UpdateManager} The UpdateManager
44585      */
44586     setUrl : function(url, params, loadOnce){
44587         if(this.refreshDelegate){
44588             this.removeListener("activate", this.refreshDelegate);
44589         }
44590         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44591         this.on("activate", this.refreshDelegate);
44592         return this.el.getUpdateManager();
44593     },
44594     
44595     _handleRefresh : function(url, params, loadOnce){
44596         if(!loadOnce || !this.loaded){
44597             var updater = this.el.getUpdateManager();
44598             updater.update(url, params, this._setLoaded.createDelegate(this));
44599         }
44600     },
44601     
44602     _setLoaded : function(){
44603         this.loaded = true;
44604     }, 
44605     
44606     /**
44607      * Returns this panel's id
44608      * @return {String} 
44609      */
44610     getId : function(){
44611         return this.el.id;
44612     },
44613     
44614     /** 
44615      * Returns this panel's element - used by regiosn to add.
44616      * @return {Roo.Element} 
44617      */
44618     getEl : function(){
44619         return this.wrapEl || this.el;
44620     },
44621     
44622     adjustForComponents : function(width, height){
44623         if(this.resizeEl != this.el){
44624             width -= this.el.getFrameWidth('lr');
44625             height -= this.el.getFrameWidth('tb');
44626         }
44627         if(this.toolbar){
44628             var te = this.toolbar.getEl();
44629             height -= te.getHeight();
44630             te.setWidth(width);
44631         }
44632         if(this.adjustments){
44633             width += this.adjustments[0];
44634             height += this.adjustments[1];
44635         }
44636         return {"width": width, "height": height};
44637     },
44638     
44639     setSize : function(width, height){
44640         if(this.fitToFrame && !this.ignoreResize(width, height)){
44641             if(this.fitContainer && this.resizeEl != this.el){
44642                 this.el.setSize(width, height);
44643             }
44644             var size = this.adjustForComponents(width, height);
44645             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44646             this.fireEvent('resize', this, size.width, size.height);
44647         }
44648     },
44649     
44650     /**
44651      * Returns this panel's title
44652      * @return {String} 
44653      */
44654     getTitle : function(){
44655         return this.title;
44656     },
44657     
44658     /**
44659      * Set this panel's title
44660      * @param {String} title
44661      */
44662     setTitle : function(title){
44663         this.title = title;
44664         if(this.region){
44665             this.region.updatePanelTitle(this, title);
44666         }
44667     },
44668     
44669     /**
44670      * Returns true is this panel was configured to be closable
44671      * @return {Boolean} 
44672      */
44673     isClosable : function(){
44674         return this.closable;
44675     },
44676     
44677     beforeSlide : function(){
44678         this.el.clip();
44679         this.resizeEl.clip();
44680     },
44681     
44682     afterSlide : function(){
44683         this.el.unclip();
44684         this.resizeEl.unclip();
44685     },
44686     
44687     /**
44688      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44689      *   Will fail silently if the {@link #setUrl} method has not been called.
44690      *   This does not activate the panel, just updates its content.
44691      */
44692     refresh : function(){
44693         if(this.refreshDelegate){
44694            this.loaded = false;
44695            this.refreshDelegate();
44696         }
44697     },
44698     
44699     /**
44700      * Destroys this panel
44701      */
44702     destroy : function(){
44703         this.el.removeAllListeners();
44704         var tempEl = document.createElement("span");
44705         tempEl.appendChild(this.el.dom);
44706         tempEl.innerHTML = "";
44707         this.el.remove();
44708         this.el = null;
44709     },
44710     
44711       /**
44712      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44713      * <pre><code>
44714
44715 layout.addxtype({
44716        xtype : 'Form',
44717        items: [ .... ]
44718    }
44719 );
44720
44721 </code></pre>
44722      * @param {Object} cfg Xtype definition of item to add.
44723      */
44724     
44725     addxtype : function(cfg) {
44726         // add form..
44727         if (cfg.xtype.match(/^Form$/)) {
44728             var el = this.el.createChild();
44729
44730             this.form = new  Roo.form.Form(cfg);
44731             
44732             
44733             if ( this.form.allItems.length) this.form.render(el.dom);
44734             return this.form;
44735         }
44736         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
44737             // views..
44738             cfg.el = this.el;
44739             // factory?
44740             var ret = new Roo[cfg.xtype](cfg);
44741             ret.render(false, ''); // render blank..
44742             return ret;
44743             
44744         }
44745         return false;
44746         
44747     }
44748 });
44749
44750 /**
44751  * @class Roo.GridPanel
44752  * @extends Roo.ContentPanel
44753  * @constructor
44754  * Create a new GridPanel.
44755  * @param {Roo.grid.Grid} grid The grid for this panel
44756  * @param {String/Object} config A string to set only the panel's title, or a config object
44757  */
44758 Roo.GridPanel = function(grid, config){
44759     
44760   
44761     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
44762         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
44763         
44764     this.wrapper.dom.appendChild(grid.getGridEl().dom);
44765     
44766     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
44767     
44768     if(this.toolbar){
44769         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
44770     }
44771     // xtype created footer. - not sure if will work as we normally have to render first..
44772     if (this.footer && !this.footer.el && this.footer.xtype) {
44773         
44774         this.footer.container = this.grid.getView().getFooterPanel(true);
44775         this.footer.dataSource = this.grid.dataSource;
44776         this.footer = Roo.factory(this.footer, Roo);
44777         
44778     }
44779     
44780     grid.monitorWindowResize = false; // turn off autosizing
44781     grid.autoHeight = false;
44782     grid.autoWidth = false;
44783     this.grid = grid;
44784     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
44785 };
44786
44787 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
44788     getId : function(){
44789         return this.grid.id;
44790     },
44791     
44792     /**
44793      * Returns the grid for this panel
44794      * @return {Roo.grid.Grid} 
44795      */
44796     getGrid : function(){
44797         return this.grid;    
44798     },
44799     
44800     setSize : function(width, height){
44801         if(!this.ignoreResize(width, height)){
44802             var grid = this.grid;
44803             var size = this.adjustForComponents(width, height);
44804             grid.getGridEl().setSize(size.width, size.height);
44805             grid.autoSize();
44806         }
44807     },
44808     
44809     beforeSlide : function(){
44810         this.grid.getView().scroller.clip();
44811     },
44812     
44813     afterSlide : function(){
44814         this.grid.getView().scroller.unclip();
44815     },
44816     
44817     destroy : function(){
44818         this.grid.destroy();
44819         delete this.grid;
44820         Roo.GridPanel.superclass.destroy.call(this); 
44821     }
44822 });
44823
44824
44825 /**
44826  * @class Roo.NestedLayoutPanel
44827  * @extends Roo.ContentPanel
44828  * @constructor
44829  * Create a new NestedLayoutPanel.
44830  * 
44831  * 
44832  * @param {Roo.BorderLayout} layout The layout for this panel
44833  * @param {String/Object} config A string to set only the title or a config object
44834  */
44835 Roo.NestedLayoutPanel = function(layout, config)
44836 {
44837     // construct with only one argument..
44838     /* FIXME - implement nicer consturctors
44839     if (layout.layout) {
44840         config = layout;
44841         layout = config.layout;
44842         delete config.layout;
44843     }
44844     if (layout.xtype && !layout.getEl) {
44845         // then layout needs constructing..
44846         layout = Roo.factory(layout, Roo);
44847     }
44848     */
44849     
44850     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
44851     
44852     layout.monitorWindowResize = false; // turn off autosizing
44853     this.layout = layout;
44854     this.layout.getEl().addClass("x-layout-nested-layout");
44855     
44856     
44857     
44858 };
44859
44860 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
44861
44862     setSize : function(width, height){
44863         if(!this.ignoreResize(width, height)){
44864             var size = this.adjustForComponents(width, height);
44865             var el = this.layout.getEl();
44866             el.setSize(size.width, size.height);
44867             var touch = el.dom.offsetWidth;
44868             this.layout.layout();
44869             // ie requires a double layout on the first pass
44870             if(Roo.isIE && !this.initialized){
44871                 this.initialized = true;
44872                 this.layout.layout();
44873             }
44874         }
44875     },
44876     
44877     // activate all subpanels if not currently active..
44878     
44879     setActiveState : function(active){
44880         this.active = active;
44881         if(!active){
44882             this.fireEvent("deactivate", this);
44883             return;
44884         }
44885         
44886         this.fireEvent("activate", this);
44887         // not sure if this should happen before or after..
44888         if (!this.layout) {
44889             return; // should not happen..
44890         }
44891         var reg = false;
44892         for (var r in this.layout.regions) {
44893             reg = this.layout.getRegion(r);
44894             if (reg.getActivePanel()) {
44895                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
44896                 reg.setActivePanel(reg.getActivePanel());
44897                 continue;
44898             }
44899             if (!reg.panels.length) {
44900                 continue;
44901             }
44902             reg.showPanel(reg.getPanel(0));
44903         }
44904         
44905         
44906         
44907         
44908     },
44909     
44910     /**
44911      * Returns the nested BorderLayout for this panel
44912      * @return {Roo.BorderLayout} 
44913      */
44914     getLayout : function(){
44915         return this.layout;
44916     },
44917     
44918      /**
44919      * Adds a xtype elements to the layout of the nested panel
44920      * <pre><code>
44921
44922 panel.addxtype({
44923        xtype : 'ContentPanel',
44924        region: 'west',
44925        items: [ .... ]
44926    }
44927 );
44928
44929 panel.addxtype({
44930         xtype : 'NestedLayoutPanel',
44931         region: 'west',
44932         layout: {
44933            center: { },
44934            west: { }   
44935         },
44936         items : [ ... list of content panels or nested layout panels.. ]
44937    }
44938 );
44939 </code></pre>
44940      * @param {Object} cfg Xtype definition of item to add.
44941      */
44942     addxtype : function(cfg) {
44943         return this.layout.addxtype(cfg);
44944     
44945     }
44946 });
44947
44948 Roo.ScrollPanel = function(el, config, content){
44949     config = config || {};
44950     config.fitToFrame = true;
44951     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
44952     
44953     this.el.dom.style.overflow = "hidden";
44954     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
44955     this.el.removeClass("x-layout-inactive-content");
44956     this.el.on("mousewheel", this.onWheel, this);
44957
44958     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
44959     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
44960     up.unselectable(); down.unselectable();
44961     up.on("click", this.scrollUp, this);
44962     down.on("click", this.scrollDown, this);
44963     up.addClassOnOver("x-scroller-btn-over");
44964     down.addClassOnOver("x-scroller-btn-over");
44965     up.addClassOnClick("x-scroller-btn-click");
44966     down.addClassOnClick("x-scroller-btn-click");
44967     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
44968
44969     this.resizeEl = this.el;
44970     this.el = wrap; this.up = up; this.down = down;
44971 };
44972
44973 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
44974     increment : 100,
44975     wheelIncrement : 5,
44976     scrollUp : function(){
44977         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
44978     },
44979
44980     scrollDown : function(){
44981         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
44982     },
44983
44984     afterScroll : function(){
44985         var el = this.resizeEl;
44986         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
44987         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44988         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44989     },
44990
44991     setSize : function(){
44992         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
44993         this.afterScroll();
44994     },
44995
44996     onWheel : function(e){
44997         var d = e.getWheelDelta();
44998         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
44999         this.afterScroll();
45000         e.stopEvent();
45001     },
45002
45003     setContent : function(content, loadScripts){
45004         this.resizeEl.update(content, loadScripts);
45005     }
45006
45007 });
45008
45009
45010
45011
45012
45013
45014
45015
45016
45017 /**
45018  * @class Roo.TreePanel
45019  * @extends Roo.ContentPanel
45020  * @constructor
45021  * Create a new TreePanel.
45022  * @param {String/Object} config A string to set only the panel's title, or a config object
45023  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45024  */
45025 Roo.TreePanel = function(config){
45026     var el = config.el;
45027     var tree = config.tree;
45028     delete config.tree; 
45029     delete config.el; // hopefull!
45030     Roo.TreePanel.superclass.constructor.call(this, el, config);
45031     var treeEl = el.createChild();
45032     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45033     //console.log(tree);
45034     this.on('activate', function()
45035     {
45036         if (this.tree.rendered) {
45037             return;
45038         }
45039         //console.log('render tree');
45040         this.tree.render();
45041     });
45042     
45043     this.on('resize',  function (cp, w, h) {
45044             this.tree.innerCt.setWidth(w);
45045             this.tree.innerCt.setHeight(h);
45046             this.tree.innerCt.setStyle('overflow-y', 'auto');
45047     });
45048
45049         
45050     
45051 };
45052
45053 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
45054
45055
45056
45057
45058
45059
45060
45061
45062
45063
45064
45065 /*
45066  * Based on:
45067  * Ext JS Library 1.1.1
45068  * Copyright(c) 2006-2007, Ext JS, LLC.
45069  *
45070  * Originally Released Under LGPL - original licence link has changed is not relivant.
45071  *
45072  * Fork - LGPL
45073  * <script type="text/javascript">
45074  */
45075  
45076
45077 /**
45078  * @class Roo.ReaderLayout
45079  * @extends Roo.BorderLayout
45080  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
45081  * center region containing two nested regions (a top one for a list view and one for item preview below),
45082  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
45083  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
45084  * expedites the setup of the overall layout and regions for this common application style.
45085  * Example:
45086  <pre><code>
45087 var reader = new Roo.ReaderLayout();
45088 var CP = Roo.ContentPanel;  // shortcut for adding
45089
45090 reader.beginUpdate();
45091 reader.add("north", new CP("north", "North"));
45092 reader.add("west", new CP("west", {title: "West"}));
45093 reader.add("east", new CP("east", {title: "East"}));
45094
45095 reader.regions.listView.add(new CP("listView", "List"));
45096 reader.regions.preview.add(new CP("preview", "Preview"));
45097 reader.endUpdate();
45098 </code></pre>
45099 * @constructor
45100 * Create a new ReaderLayout
45101 * @param {Object} config Configuration options
45102 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
45103 * document.body if omitted)
45104 */
45105 Roo.ReaderLayout = function(config, renderTo){
45106     var c = config || {size:{}};
45107     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
45108         north: c.north !== false ? Roo.apply({
45109             split:false,
45110             initialSize: 32,
45111             titlebar: false
45112         }, c.north) : false,
45113         west: c.west !== false ? Roo.apply({
45114             split:true,
45115             initialSize: 200,
45116             minSize: 175,
45117             maxSize: 400,
45118             titlebar: true,
45119             collapsible: true,
45120             animate: true,
45121             margins:{left:5,right:0,bottom:5,top:5},
45122             cmargins:{left:5,right:5,bottom:5,top:5}
45123         }, c.west) : false,
45124         east: c.east !== false ? Roo.apply({
45125             split:true,
45126             initialSize: 200,
45127             minSize: 175,
45128             maxSize: 400,
45129             titlebar: true,
45130             collapsible: true,
45131             animate: true,
45132             margins:{left:0,right:5,bottom:5,top:5},
45133             cmargins:{left:5,right:5,bottom:5,top:5}
45134         }, c.east) : false,
45135         center: Roo.apply({
45136             tabPosition: 'top',
45137             autoScroll:false,
45138             closeOnTab: true,
45139             titlebar:false,
45140             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
45141         }, c.center)
45142     });
45143
45144     this.el.addClass('x-reader');
45145
45146     this.beginUpdate();
45147
45148     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
45149         south: c.preview !== false ? Roo.apply({
45150             split:true,
45151             initialSize: 200,
45152             minSize: 100,
45153             autoScroll:true,
45154             collapsible:true,
45155             titlebar: true,
45156             cmargins:{top:5,left:0, right:0, bottom:0}
45157         }, c.preview) : false,
45158         center: Roo.apply({
45159             autoScroll:false,
45160             titlebar:false,
45161             minHeight:200
45162         }, c.listView)
45163     });
45164     this.add('center', new Roo.NestedLayoutPanel(inner,
45165             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
45166
45167     this.endUpdate();
45168
45169     this.regions.preview = inner.getRegion('south');
45170     this.regions.listView = inner.getRegion('center');
45171 };
45172
45173 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
45174  * Based on:
45175  * Ext JS Library 1.1.1
45176  * Copyright(c) 2006-2007, Ext JS, LLC.
45177  *
45178  * Originally Released Under LGPL - original licence link has changed is not relivant.
45179  *
45180  * Fork - LGPL
45181  * <script type="text/javascript">
45182  */
45183  
45184 /**
45185  * @class Roo.grid.Grid
45186  * @extends Roo.util.Observable
45187  * This class represents the primary interface of a component based grid control.
45188  * <br><br>Usage:<pre><code>
45189  var grid = new Roo.grid.Grid("my-container-id", {
45190      ds: myDataStore,
45191      cm: myColModel,
45192      selModel: mySelectionModel,
45193      autoSizeColumns: true,
45194      monitorWindowResize: false,
45195      trackMouseOver: true
45196  });
45197  // set any options
45198  grid.render();
45199  * </code></pre>
45200  * <b>Common Problems:</b><br/>
45201  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
45202  * element will correct this<br/>
45203  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
45204  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
45205  * are unpredictable.<br/>
45206  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
45207  * grid to calculate dimensions/offsets.<br/>
45208   * @constructor
45209  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
45210  * The container MUST have some type of size defined for the grid to fill. The container will be
45211  * automatically set to position relative if it isn't already.
45212  * @param {Object} config A config object that sets properties on this grid.
45213  */
45214 Roo.grid.Grid = function(container, config){
45215         // initialize the container
45216         this.container = Roo.get(container);
45217         this.container.update("");
45218         this.container.setStyle("overflow", "hidden");
45219     this.container.addClass('x-grid-container');
45220
45221     this.id = this.container.id;
45222
45223     Roo.apply(this, config);
45224     // check and correct shorthanded configs
45225     if(this.ds){
45226         this.dataSource = this.ds;
45227         delete this.ds;
45228     }
45229     if(this.cm){
45230         this.colModel = this.cm;
45231         delete this.cm;
45232     }
45233     if(this.sm){
45234         this.selModel = this.sm;
45235         delete this.sm;
45236     }
45237
45238     if (this.selModel) {
45239         this.selModel = Roo.factory(this.selModel, Roo.grid);
45240         this.sm = this.selModel;
45241         this.sm.xmodule = this.xmodule || false;
45242     }
45243     if (typeof(this.colModel.config) == 'undefined') {
45244         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45245         this.cm = this.colModel;
45246         this.cm.xmodule = this.xmodule || false;
45247     }
45248     if (this.dataSource) {
45249         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45250         this.ds = this.dataSource;
45251         this.ds.xmodule = this.xmodule || false;
45252         
45253     }
45254     
45255     
45256     
45257     if(this.width){
45258         this.container.setWidth(this.width);
45259     }
45260
45261     if(this.height){
45262         this.container.setHeight(this.height);
45263     }
45264     /** @private */
45265         this.addEvents({
45266             // raw events
45267             /**
45268              * @event click
45269              * The raw click event for the entire grid.
45270              * @param {Roo.EventObject} e
45271              */
45272             "click" : true,
45273             /**
45274              * @event dblclick
45275              * The raw dblclick event for the entire grid.
45276              * @param {Roo.EventObject} e
45277              */
45278             "dblclick" : true,
45279             /**
45280              * @event contextmenu
45281              * The raw contextmenu event for the entire grid.
45282              * @param {Roo.EventObject} e
45283              */
45284             "contextmenu" : true,
45285             /**
45286              * @event mousedown
45287              * The raw mousedown event for the entire grid.
45288              * @param {Roo.EventObject} e
45289              */
45290             "mousedown" : true,
45291             /**
45292              * @event mouseup
45293              * The raw mouseup event for the entire grid.
45294              * @param {Roo.EventObject} e
45295              */
45296             "mouseup" : true,
45297             /**
45298              * @event mouseover
45299              * The raw mouseover event for the entire grid.
45300              * @param {Roo.EventObject} e
45301              */
45302             "mouseover" : true,
45303             /**
45304              * @event mouseout
45305              * The raw mouseout event for the entire grid.
45306              * @param {Roo.EventObject} e
45307              */
45308             "mouseout" : true,
45309             /**
45310              * @event keypress
45311              * The raw keypress event for the entire grid.
45312              * @param {Roo.EventObject} e
45313              */
45314             "keypress" : true,
45315             /**
45316              * @event keydown
45317              * The raw keydown event for the entire grid.
45318              * @param {Roo.EventObject} e
45319              */
45320             "keydown" : true,
45321
45322             // custom events
45323
45324             /**
45325              * @event cellclick
45326              * Fires when a cell is clicked
45327              * @param {Grid} this
45328              * @param {Number} rowIndex
45329              * @param {Number} columnIndex
45330              * @param {Roo.EventObject} e
45331              */
45332             "cellclick" : true,
45333             /**
45334              * @event celldblclick
45335              * Fires when a cell is double clicked
45336              * @param {Grid} this
45337              * @param {Number} rowIndex
45338              * @param {Number} columnIndex
45339              * @param {Roo.EventObject} e
45340              */
45341             "celldblclick" : true,
45342             /**
45343              * @event rowclick
45344              * Fires when a row is clicked
45345              * @param {Grid} this
45346              * @param {Number} rowIndex
45347              * @param {Roo.EventObject} e
45348              */
45349             "rowclick" : true,
45350             /**
45351              * @event rowdblclick
45352              * Fires when a row is double clicked
45353              * @param {Grid} this
45354              * @param {Number} rowIndex
45355              * @param {Roo.EventObject} e
45356              */
45357             "rowdblclick" : true,
45358             /**
45359              * @event headerclick
45360              * Fires when a header is clicked
45361              * @param {Grid} this
45362              * @param {Number} columnIndex
45363              * @param {Roo.EventObject} e
45364              */
45365             "headerclick" : true,
45366             /**
45367              * @event headerdblclick
45368              * Fires when a header cell is double clicked
45369              * @param {Grid} this
45370              * @param {Number} columnIndex
45371              * @param {Roo.EventObject} e
45372              */
45373             "headerdblclick" : true,
45374             /**
45375              * @event rowcontextmenu
45376              * Fires when a row is right clicked
45377              * @param {Grid} this
45378              * @param {Number} rowIndex
45379              * @param {Roo.EventObject} e
45380              */
45381             "rowcontextmenu" : true,
45382             /**
45383          * @event cellcontextmenu
45384          * Fires when a cell is right clicked
45385          * @param {Grid} this
45386          * @param {Number} rowIndex
45387          * @param {Number} cellIndex
45388          * @param {Roo.EventObject} e
45389          */
45390          "cellcontextmenu" : true,
45391             /**
45392              * @event headercontextmenu
45393              * Fires when a header is right clicked
45394              * @param {Grid} this
45395              * @param {Number} columnIndex
45396              * @param {Roo.EventObject} e
45397              */
45398             "headercontextmenu" : true,
45399             /**
45400              * @event bodyscroll
45401              * Fires when the body element is scrolled
45402              * @param {Number} scrollLeft
45403              * @param {Number} scrollTop
45404              */
45405             "bodyscroll" : true,
45406             /**
45407              * @event columnresize
45408              * Fires when the user resizes a column
45409              * @param {Number} columnIndex
45410              * @param {Number} newSize
45411              */
45412             "columnresize" : true,
45413             /**
45414              * @event columnmove
45415              * Fires when the user moves a column
45416              * @param {Number} oldIndex
45417              * @param {Number} newIndex
45418              */
45419             "columnmove" : true,
45420             /**
45421              * @event startdrag
45422              * Fires when row(s) start being dragged
45423              * @param {Grid} this
45424              * @param {Roo.GridDD} dd The drag drop object
45425              * @param {event} e The raw browser event
45426              */
45427             "startdrag" : true,
45428             /**
45429              * @event enddrag
45430              * Fires when a drag operation is complete
45431              * @param {Grid} this
45432              * @param {Roo.GridDD} dd The drag drop object
45433              * @param {event} e The raw browser event
45434              */
45435             "enddrag" : true,
45436             /**
45437              * @event dragdrop
45438              * Fires when dragged row(s) are dropped on a valid DD target
45439              * @param {Grid} this
45440              * @param {Roo.GridDD} dd The drag drop object
45441              * @param {String} targetId The target drag drop object
45442              * @param {event} e The raw browser event
45443              */
45444             "dragdrop" : true,
45445             /**
45446              * @event dragover
45447              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
45448              * @param {Grid} this
45449              * @param {Roo.GridDD} dd The drag drop object
45450              * @param {String} targetId The target drag drop object
45451              * @param {event} e The raw browser event
45452              */
45453             "dragover" : true,
45454             /**
45455              * @event dragenter
45456              *  Fires when the dragged row(s) first cross another DD target while being dragged
45457              * @param {Grid} this
45458              * @param {Roo.GridDD} dd The drag drop object
45459              * @param {String} targetId The target drag drop object
45460              * @param {event} e The raw browser event
45461              */
45462             "dragenter" : true,
45463             /**
45464              * @event dragout
45465              * Fires when the dragged row(s) leave another DD target while being dragged
45466              * @param {Grid} this
45467              * @param {Roo.GridDD} dd The drag drop object
45468              * @param {String} targetId The target drag drop object
45469              * @param {event} e The raw browser event
45470              */
45471             "dragout" : true,
45472         /**
45473          * @event render
45474          * Fires when the grid is rendered
45475          * @param {Grid} grid
45476          */
45477         render : true
45478     });
45479
45480     Roo.grid.Grid.superclass.constructor.call(this);
45481 };
45482 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
45483     /**
45484      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
45485          */
45486         minColumnWidth : 25,
45487
45488     /**
45489          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
45490          * <b>on initial render.</b> It is more efficient to explicitly size the columns
45491          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
45492          */
45493         autoSizeColumns : false,
45494
45495         /**
45496          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
45497          */
45498         autoSizeHeaders : true,
45499
45500         /**
45501          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
45502          */
45503         monitorWindowResize : true,
45504
45505         /**
45506          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
45507          * rows measured to get a columns size. Default is 0 (all rows).
45508          */
45509         maxRowsToMeasure : 0,
45510
45511         /**
45512          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
45513          */
45514         trackMouseOver : true,
45515
45516         /**
45517          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
45518          */
45519         enableDragDrop : false,
45520
45521         /**
45522          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
45523          */
45524         enableColumnMove : true,
45525
45526         /**
45527          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
45528          */
45529         enableColumnHide : true,
45530
45531         /**
45532          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
45533          */
45534         enableRowHeightSync : false,
45535
45536         /**
45537          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
45538          */
45539         stripeRows : true,
45540
45541         /**
45542          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
45543          */
45544         autoHeight : false,
45545
45546     /**
45547      * @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.
45548      */
45549     autoExpandColumn : false,
45550
45551     /**
45552     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
45553     * Default is 50.
45554     */
45555     autoExpandMin : 50,
45556
45557     /**
45558     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
45559     */
45560     autoExpandMax : 1000,
45561
45562     /**
45563          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
45564          */
45565         view : null,
45566
45567         /**
45568      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
45569          */
45570         loadMask : false,
45571
45572     // private
45573     rendered : false,
45574
45575     /**
45576     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
45577     * of a fixed width. Default is false.
45578     */
45579     /**
45580     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
45581     */
45582     /**
45583      * Called once after all setup has been completed and the grid is ready to be rendered.
45584      * @return {Roo.grid.Grid} this
45585      */
45586     render : function(){
45587         var c = this.container;
45588         // try to detect autoHeight/width mode
45589         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
45590             this.autoHeight = true;
45591         }
45592         var view = this.getView();
45593         view.init(this);
45594
45595         c.on("click", this.onClick, this);
45596         c.on("dblclick", this.onDblClick, this);
45597         c.on("contextmenu", this.onContextMenu, this);
45598         c.on("keydown", this.onKeyDown, this);
45599
45600         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
45601
45602         this.getSelectionModel().init(this);
45603
45604         view.render();
45605
45606         if(this.loadMask){
45607             this.loadMask = new Roo.LoadMask(this.container,
45608                     Roo.apply({store:this.dataSource}, this.loadMask));
45609         }
45610         
45611         
45612         if (this.toolbar && this.toolbar.xtype) {
45613             this.toolbar.container = this.getView().getHeaderPanel(true);
45614             this.toolbar = new Ext.Toolbar(this.toolbar);
45615         }
45616         if (this.footer && this.footer.xtype) {
45617             this.footer.dataSource = this.getDataSource();
45618             this.footer.container = this.getView().getFooterPanel(true);
45619             this.footer = Roo.factory(this.footer, Roo);
45620         }
45621         this.rendered = true;
45622         this.fireEvent('render', this);
45623         return this;
45624     },
45625
45626         /**
45627          * Reconfigures the grid to use a different Store and Column Model.
45628          * The View will be bound to the new objects and refreshed.
45629          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
45630          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
45631          */
45632     reconfigure : function(dataSource, colModel){
45633         if(this.loadMask){
45634             this.loadMask.destroy();
45635             this.loadMask = new Roo.LoadMask(this.container,
45636                     Roo.apply({store:dataSource}, this.loadMask));
45637         }
45638         this.view.bind(dataSource, colModel);
45639         this.dataSource = dataSource;
45640         this.colModel = colModel;
45641         this.view.refresh(true);
45642     },
45643
45644     // private
45645     onKeyDown : function(e){
45646         this.fireEvent("keydown", e);
45647     },
45648
45649     /**
45650      * Destroy this grid.
45651      * @param {Boolean} removeEl True to remove the element
45652      */
45653     destroy : function(removeEl, keepListeners){
45654         if(this.loadMask){
45655             this.loadMask.destroy();
45656         }
45657         var c = this.container;
45658         c.removeAllListeners();
45659         this.view.destroy();
45660         this.colModel.purgeListeners();
45661         if(!keepListeners){
45662             this.purgeListeners();
45663         }
45664         c.update("");
45665         if(removeEl === true){
45666             c.remove();
45667         }
45668     },
45669
45670     // private
45671     processEvent : function(name, e){
45672         this.fireEvent(name, e);
45673         var t = e.getTarget();
45674         var v = this.view;
45675         var header = v.findHeaderIndex(t);
45676         if(header !== false){
45677             this.fireEvent("header" + name, this, header, e);
45678         }else{
45679             var row = v.findRowIndex(t);
45680             var cell = v.findCellIndex(t);
45681             if(row !== false){
45682                 this.fireEvent("row" + name, this, row, e);
45683                 if(cell !== false){
45684                     this.fireEvent("cell" + name, this, row, cell, e);
45685                 }
45686             }
45687         }
45688     },
45689
45690     // private
45691     onClick : function(e){
45692         this.processEvent("click", e);
45693     },
45694
45695     // private
45696     onContextMenu : function(e, t){
45697         this.processEvent("contextmenu", e);
45698     },
45699
45700     // private
45701     onDblClick : function(e){
45702         this.processEvent("dblclick", e);
45703     },
45704
45705     // private
45706     walkCells : function(row, col, step, fn, scope){
45707         var cm = this.colModel, clen = cm.getColumnCount();
45708         var ds = this.dataSource, rlen = ds.getCount(), first = true;
45709         if(step < 0){
45710             if(col < 0){
45711                 row--;
45712                 first = false;
45713             }
45714             while(row >= 0){
45715                 if(!first){
45716                     col = clen-1;
45717                 }
45718                 first = false;
45719                 while(col >= 0){
45720                     if(fn.call(scope || this, row, col, cm) === true){
45721                         return [row, col];
45722                     }
45723                     col--;
45724                 }
45725                 row--;
45726             }
45727         } else {
45728             if(col >= clen){
45729                 row++;
45730                 first = false;
45731             }
45732             while(row < rlen){
45733                 if(!first){
45734                     col = 0;
45735                 }
45736                 first = false;
45737                 while(col < clen){
45738                     if(fn.call(scope || this, row, col, cm) === true){
45739                         return [row, col];
45740                     }
45741                     col++;
45742                 }
45743                 row++;
45744             }
45745         }
45746         return null;
45747     },
45748
45749     // private
45750     getSelections : function(){
45751         return this.selModel.getSelections();
45752     },
45753
45754     /**
45755      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
45756      * but if manual update is required this method will initiate it.
45757      */
45758     autoSize : function(){
45759         if(this.rendered){
45760             this.view.layout();
45761             if(this.view.adjustForScroll){
45762                 this.view.adjustForScroll();
45763             }
45764         }
45765     },
45766
45767     /**
45768      * Returns the grid's underlying element.
45769      * @return {Element} The element
45770      */
45771     getGridEl : function(){
45772         return this.container;
45773     },
45774
45775     // private for compatibility, overridden by editor grid
45776     stopEditing : function(){},
45777
45778     /**
45779      * Returns the grid's SelectionModel.
45780      * @return {SelectionModel}
45781      */
45782     getSelectionModel : function(){
45783         if(!this.selModel){
45784             this.selModel = new Roo.grid.RowSelectionModel();
45785         }
45786         return this.selModel;
45787     },
45788
45789     /**
45790      * Returns the grid's DataSource.
45791      * @return {DataSource}
45792      */
45793     getDataSource : function(){
45794         return this.dataSource;
45795     },
45796
45797     /**
45798      * Returns the grid's ColumnModel.
45799      * @return {ColumnModel}
45800      */
45801     getColumnModel : function(){
45802         return this.colModel;
45803     },
45804
45805     /**
45806      * Returns the grid's GridView object.
45807      * @return {GridView}
45808      */
45809     getView : function(){
45810         if(!this.view){
45811             this.view = new Roo.grid.GridView(this.viewConfig);
45812         }
45813         return this.view;
45814     },
45815     /**
45816      * Called to get grid's drag proxy text, by default returns this.ddText.
45817      * @return {String}
45818      */
45819     getDragDropText : function(){
45820         var count = this.selModel.getCount();
45821         return String.format(this.ddText, count, count == 1 ? '' : 's');
45822     }
45823 });
45824 /**
45825  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
45826  * %0 is replaced with the number of selected rows.
45827  * @type String
45828  */
45829 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
45830  * Based on:
45831  * Ext JS Library 1.1.1
45832  * Copyright(c) 2006-2007, Ext JS, LLC.
45833  *
45834  * Originally Released Under LGPL - original licence link has changed is not relivant.
45835  *
45836  * Fork - LGPL
45837  * <script type="text/javascript">
45838  */
45839  
45840 Roo.grid.AbstractGridView = function(){
45841         this.grid = null;
45842         
45843         this.events = {
45844             "beforerowremoved" : true,
45845             "beforerowsinserted" : true,
45846             "beforerefresh" : true,
45847             "rowremoved" : true,
45848             "rowsinserted" : true,
45849             "rowupdated" : true,
45850             "refresh" : true
45851         };
45852     Roo.grid.AbstractGridView.superclass.constructor.call(this);
45853 };
45854
45855 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
45856     rowClass : "x-grid-row",
45857     cellClass : "x-grid-cell",
45858     tdClass : "x-grid-td",
45859     hdClass : "x-grid-hd",
45860     splitClass : "x-grid-hd-split",
45861     
45862         init: function(grid){
45863         this.grid = grid;
45864                 var cid = this.grid.getGridEl().id;
45865         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
45866         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
45867         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
45868         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
45869         },
45870         
45871         getColumnRenderers : function(){
45872         var renderers = [];
45873         var cm = this.grid.colModel;
45874         var colCount = cm.getColumnCount();
45875         for(var i = 0; i < colCount; i++){
45876             renderers[i] = cm.getRenderer(i);
45877         }
45878         return renderers;
45879     },
45880     
45881     getColumnIds : function(){
45882         var ids = [];
45883         var cm = this.grid.colModel;
45884         var colCount = cm.getColumnCount();
45885         for(var i = 0; i < colCount; i++){
45886             ids[i] = cm.getColumnId(i);
45887         }
45888         return ids;
45889     },
45890     
45891     getDataIndexes : function(){
45892         if(!this.indexMap){
45893             this.indexMap = this.buildIndexMap();
45894         }
45895         return this.indexMap.colToData;
45896     },
45897     
45898     getColumnIndexByDataIndex : function(dataIndex){
45899         if(!this.indexMap){
45900             this.indexMap = this.buildIndexMap();
45901         }
45902         return this.indexMap.dataToCol[dataIndex];
45903     },
45904     
45905     /**
45906      * Set a css style for a column dynamically. 
45907      * @param {Number} colIndex The index of the column
45908      * @param {String} name The css property name
45909      * @param {String} value The css value
45910      */
45911     setCSSStyle : function(colIndex, name, value){
45912         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
45913         Roo.util.CSS.updateRule(selector, name, value);
45914     },
45915     
45916     generateRules : function(cm){
45917         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
45918         Roo.util.CSS.removeStyleSheet(rulesId);
45919         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
45920             var cid = cm.getColumnId(i);
45921             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
45922                          this.tdSelector, cid, " {\n}\n",
45923                          this.hdSelector, cid, " {\n}\n",
45924                          this.splitSelector, cid, " {\n}\n");
45925         }
45926         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
45927     }
45928 });/*
45929  * Based on:
45930  * Ext JS Library 1.1.1
45931  * Copyright(c) 2006-2007, Ext JS, LLC.
45932  *
45933  * Originally Released Under LGPL - original licence link has changed is not relivant.
45934  *
45935  * Fork - LGPL
45936  * <script type="text/javascript">
45937  */
45938
45939 // private
45940 // This is a support class used internally by the Grid components
45941 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
45942     this.grid = grid;
45943     this.view = grid.getView();
45944     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45945     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
45946     if(hd2){
45947         this.setHandleElId(Roo.id(hd));
45948         this.setOuterHandleElId(Roo.id(hd2));
45949     }
45950     this.scroll = false;
45951 };
45952 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
45953     maxDragWidth: 120,
45954     getDragData : function(e){
45955         var t = Roo.lib.Event.getTarget(e);
45956         var h = this.view.findHeaderCell(t);
45957         if(h){
45958             return {ddel: h.firstChild, header:h};
45959         }
45960         return false;
45961     },
45962
45963     onInitDrag : function(e){
45964         this.view.headersDisabled = true;
45965         var clone = this.dragData.ddel.cloneNode(true);
45966         clone.id = Roo.id();
45967         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
45968         this.proxy.update(clone);
45969         return true;
45970     },
45971
45972     afterValidDrop : function(){
45973         var v = this.view;
45974         setTimeout(function(){
45975             v.headersDisabled = false;
45976         }, 50);
45977     },
45978
45979     afterInvalidDrop : function(){
45980         var v = this.view;
45981         setTimeout(function(){
45982             v.headersDisabled = false;
45983         }, 50);
45984     }
45985 });
45986 /*
45987  * Based on:
45988  * Ext JS Library 1.1.1
45989  * Copyright(c) 2006-2007, Ext JS, LLC.
45990  *
45991  * Originally Released Under LGPL - original licence link has changed is not relivant.
45992  *
45993  * Fork - LGPL
45994  * <script type="text/javascript">
45995  */
45996 // private
45997 // This is a support class used internally by the Grid components
45998 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
45999     this.grid = grid;
46000     this.view = grid.getView();
46001     // split the proxies so they don't interfere with mouse events
46002     this.proxyTop = Roo.DomHelper.append(document.body, {
46003         cls:"col-move-top", html:"&#160;"
46004     }, true);
46005     this.proxyBottom = Roo.DomHelper.append(document.body, {
46006         cls:"col-move-bottom", html:"&#160;"
46007     }, true);
46008     this.proxyTop.hide = this.proxyBottom.hide = function(){
46009         this.setLeftTop(-100,-100);
46010         this.setStyle("visibility", "hidden");
46011     };
46012     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46013     // temporarily disabled
46014     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
46015     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
46016 };
46017 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
46018     proxyOffsets : [-4, -9],
46019     fly: Roo.Element.fly,
46020
46021     getTargetFromEvent : function(e){
46022         var t = Roo.lib.Event.getTarget(e);
46023         var cindex = this.view.findCellIndex(t);
46024         if(cindex !== false){
46025             return this.view.getHeaderCell(cindex);
46026         }
46027     },
46028
46029     nextVisible : function(h){
46030         var v = this.view, cm = this.grid.colModel;
46031         h = h.nextSibling;
46032         while(h){
46033             if(!cm.isHidden(v.getCellIndex(h))){
46034                 return h;
46035             }
46036             h = h.nextSibling;
46037         }
46038         return null;
46039     },
46040
46041     prevVisible : function(h){
46042         var v = this.view, cm = this.grid.colModel;
46043         h = h.prevSibling;
46044         while(h){
46045             if(!cm.isHidden(v.getCellIndex(h))){
46046                 return h;
46047             }
46048             h = h.prevSibling;
46049         }
46050         return null;
46051     },
46052
46053     positionIndicator : function(h, n, e){
46054         var x = Roo.lib.Event.getPageX(e);
46055         var r = Roo.lib.Dom.getRegion(n.firstChild);
46056         var px, pt, py = r.top + this.proxyOffsets[1];
46057         if((r.right - x) <= (r.right-r.left)/2){
46058             px = r.right+this.view.borderWidth;
46059             pt = "after";
46060         }else{
46061             px = r.left;
46062             pt = "before";
46063         }
46064         var oldIndex = this.view.getCellIndex(h);
46065         var newIndex = this.view.getCellIndex(n);
46066
46067         if(this.grid.colModel.isFixed(newIndex)){
46068             return false;
46069         }
46070
46071         var locked = this.grid.colModel.isLocked(newIndex);
46072
46073         if(pt == "after"){
46074             newIndex++;
46075         }
46076         if(oldIndex < newIndex){
46077             newIndex--;
46078         }
46079         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
46080             return false;
46081         }
46082         px +=  this.proxyOffsets[0];
46083         this.proxyTop.setLeftTop(px, py);
46084         this.proxyTop.show();
46085         if(!this.bottomOffset){
46086             this.bottomOffset = this.view.mainHd.getHeight();
46087         }
46088         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
46089         this.proxyBottom.show();
46090         return pt;
46091     },
46092
46093     onNodeEnter : function(n, dd, e, data){
46094         if(data.header != n){
46095             this.positionIndicator(data.header, n, e);
46096         }
46097     },
46098
46099     onNodeOver : function(n, dd, e, data){
46100         var result = false;
46101         if(data.header != n){
46102             result = this.positionIndicator(data.header, n, e);
46103         }
46104         if(!result){
46105             this.proxyTop.hide();
46106             this.proxyBottom.hide();
46107         }
46108         return result ? this.dropAllowed : this.dropNotAllowed;
46109     },
46110
46111     onNodeOut : function(n, dd, e, data){
46112         this.proxyTop.hide();
46113         this.proxyBottom.hide();
46114     },
46115
46116     onNodeDrop : function(n, dd, e, data){
46117         var h = data.header;
46118         if(h != n){
46119             var cm = this.grid.colModel;
46120             var x = Roo.lib.Event.getPageX(e);
46121             var r = Roo.lib.Dom.getRegion(n.firstChild);
46122             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
46123             var oldIndex = this.view.getCellIndex(h);
46124             var newIndex = this.view.getCellIndex(n);
46125             var locked = cm.isLocked(newIndex);
46126             if(pt == "after"){
46127                 newIndex++;
46128             }
46129             if(oldIndex < newIndex){
46130                 newIndex--;
46131             }
46132             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
46133                 return false;
46134             }
46135             cm.setLocked(oldIndex, locked, true);
46136             cm.moveColumn(oldIndex, newIndex);
46137             this.grid.fireEvent("columnmove", oldIndex, newIndex);
46138             return true;
46139         }
46140         return false;
46141     }
46142 });
46143 /*
46144  * Based on:
46145  * Ext JS Library 1.1.1
46146  * Copyright(c) 2006-2007, Ext JS, LLC.
46147  *
46148  * Originally Released Under LGPL - original licence link has changed is not relivant.
46149  *
46150  * Fork - LGPL
46151  * <script type="text/javascript">
46152  */
46153   
46154 /**
46155  * @class Roo.grid.GridView
46156  * @extends Roo.util.Observable
46157  *
46158  * @constructor
46159  * @param {Object} config
46160  */
46161 Roo.grid.GridView = function(config){
46162     Roo.grid.GridView.superclass.constructor.call(this);
46163     this.el = null;
46164
46165     Roo.apply(this, config);
46166 };
46167
46168 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
46169
46170     /**
46171      * Override this function to apply custom css classes to rows during rendering
46172      * @param {Record} record The record
46173      * @param {Number} index
46174      * @method getRowClass
46175      */
46176     rowClass : "x-grid-row",
46177
46178     cellClass : "x-grid-col",
46179
46180     tdClass : "x-grid-td",
46181
46182     hdClass : "x-grid-hd",
46183
46184     splitClass : "x-grid-split",
46185
46186     sortClasses : ["sort-asc", "sort-desc"],
46187
46188     enableMoveAnim : false,
46189
46190     hlColor: "C3DAF9",
46191
46192     dh : Roo.DomHelper,
46193
46194     fly : Roo.Element.fly,
46195
46196     css : Roo.util.CSS,
46197
46198     borderWidth: 1,
46199
46200     splitOffset: 3,
46201
46202     scrollIncrement : 22,
46203
46204     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
46205
46206     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
46207
46208     bind : function(ds, cm){
46209         if(this.ds){
46210             this.ds.un("load", this.onLoad, this);
46211             this.ds.un("datachanged", this.onDataChange, this);
46212             this.ds.un("add", this.onAdd, this);
46213             this.ds.un("remove", this.onRemove, this);
46214             this.ds.un("update", this.onUpdate, this);
46215             this.ds.un("clear", this.onClear, this);
46216         }
46217         if(ds){
46218             ds.on("load", this.onLoad, this);
46219             ds.on("datachanged", this.onDataChange, this);
46220             ds.on("add", this.onAdd, this);
46221             ds.on("remove", this.onRemove, this);
46222             ds.on("update", this.onUpdate, this);
46223             ds.on("clear", this.onClear, this);
46224         }
46225         this.ds = ds;
46226
46227         if(this.cm){
46228             this.cm.un("widthchange", this.onColWidthChange, this);
46229             this.cm.un("headerchange", this.onHeaderChange, this);
46230             this.cm.un("hiddenchange", this.onHiddenChange, this);
46231             this.cm.un("columnmoved", this.onColumnMove, this);
46232             this.cm.un("columnlockchange", this.onColumnLock, this);
46233         }
46234         if(cm){
46235             this.generateRules(cm);
46236             cm.on("widthchange", this.onColWidthChange, this);
46237             cm.on("headerchange", this.onHeaderChange, this);
46238             cm.on("hiddenchange", this.onHiddenChange, this);
46239             cm.on("columnmoved", this.onColumnMove, this);
46240             cm.on("columnlockchange", this.onColumnLock, this);
46241         }
46242         this.cm = cm;
46243     },
46244
46245     init: function(grid){
46246                 Roo.grid.GridView.superclass.init.call(this, grid);
46247
46248                 this.bind(grid.dataSource, grid.colModel);
46249
46250             grid.on("headerclick", this.handleHeaderClick, this);
46251
46252         if(grid.trackMouseOver){
46253             grid.on("mouseover", this.onRowOver, this);
46254                 grid.on("mouseout", this.onRowOut, this);
46255             }
46256             grid.cancelTextSelection = function(){};
46257                 this.gridId = grid.id;
46258
46259                 var tpls = this.templates || {};
46260
46261                 if(!tpls.master){
46262                     tpls.master = new Roo.Template(
46263                        '<div class="x-grid" hidefocus="true">',
46264                           '<div class="x-grid-topbar"></div>',
46265                           '<div class="x-grid-scroller"><div></div></div>',
46266                           '<div class="x-grid-locked">',
46267                               '<div class="x-grid-header">{lockedHeader}</div>',
46268                               '<div class="x-grid-body">{lockedBody}</div>',
46269                           "</div>",
46270                           '<div class="x-grid-viewport">',
46271                               '<div class="x-grid-header">{header}</div>',
46272                               '<div class="x-grid-body">{body}</div>',
46273                           "</div>",
46274                           '<div class="x-grid-bottombar"></div>',
46275                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46276                           '<div class="x-grid-resize-proxy">&#160;</div>',
46277                        "</div>"
46278                     );
46279                     tpls.master.disableformats = true;
46280                 }
46281
46282                 if(!tpls.header){
46283                     tpls.header = new Roo.Template(
46284                        '<table border="0" cellspacing="0" cellpadding="0">',
46285                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46286                        "</table>{splits}"
46287                     );
46288                     tpls.header.disableformats = true;
46289                 }
46290                 tpls.header.compile();
46291
46292                 if(!tpls.hcell){
46293                     tpls.hcell = new Roo.Template(
46294                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
46295                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
46296                         "</div></td>"
46297                      );
46298                      tpls.hcell.disableFormats = true;
46299                 }
46300                 tpls.hcell.compile();
46301
46302                 if(!tpls.hsplit){
46303                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
46304                     tpls.hsplit.disableFormats = true;
46305                 }
46306                 tpls.hsplit.compile();
46307
46308                 if(!tpls.body){
46309                     tpls.body = new Roo.Template(
46310                        '<table border="0" cellspacing="0" cellpadding="0">',
46311                        "<tbody>{rows}</tbody>",
46312                        "</table>"
46313                     );
46314                     tpls.body.disableFormats = true;
46315                 }
46316                 tpls.body.compile();
46317
46318                 if(!tpls.row){
46319                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
46320                     tpls.row.disableFormats = true;
46321                 }
46322                 tpls.row.compile();
46323
46324                 if(!tpls.cell){
46325                     tpls.cell = new Roo.Template(
46326                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
46327                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
46328                         "</td>"
46329                     );
46330             tpls.cell.disableFormats = true;
46331         }
46332                 tpls.cell.compile();
46333
46334                 this.templates = tpls;
46335         },
46336
46337         // remap these for backwards compat
46338     onColWidthChange : function(){
46339         this.updateColumns.apply(this, arguments);
46340     },
46341     onHeaderChange : function(){
46342         this.updateHeaders.apply(this, arguments);
46343     }, 
46344     onHiddenChange : function(){
46345         this.handleHiddenChange.apply(this, arguments);
46346     },
46347     onColumnMove : function(){
46348         this.handleColumnMove.apply(this, arguments);
46349     },
46350     onColumnLock : function(){
46351         this.handleLockChange.apply(this, arguments);
46352     },
46353
46354     onDataChange : function(){
46355         this.refresh();
46356         this.updateHeaderSortState();
46357     },
46358
46359         onClear : function(){
46360         this.refresh();
46361     },
46362
46363         onUpdate : function(ds, record){
46364         this.refreshRow(record);
46365     },
46366
46367     refreshRow : function(record){
46368         var ds = this.ds, index;
46369         if(typeof record == 'number'){
46370             index = record;
46371             record = ds.getAt(index);
46372         }else{
46373             index = ds.indexOf(record);
46374         }
46375         this.insertRows(ds, index, index, true);
46376         this.onRemove(ds, record, index+1, true);
46377         this.syncRowHeights(index, index);
46378         this.layout();
46379         this.fireEvent("rowupdated", this, index, record);
46380     },
46381
46382     onAdd : function(ds, records, index){
46383         this.insertRows(ds, index, index + (records.length-1));
46384     },
46385
46386     onRemove : function(ds, record, index, isUpdate){
46387         if(isUpdate !== true){
46388             this.fireEvent("beforerowremoved", this, index, record);
46389         }
46390         var bt = this.getBodyTable(), lt = this.getLockedTable();
46391         if(bt.rows[index]){
46392             bt.firstChild.removeChild(bt.rows[index]);
46393         }
46394         if(lt.rows[index]){
46395             lt.firstChild.removeChild(lt.rows[index]);
46396         }
46397         if(isUpdate !== true){
46398             this.stripeRows(index);
46399             this.syncRowHeights(index, index);
46400             this.layout();
46401             this.fireEvent("rowremoved", this, index, record);
46402         }
46403     },
46404
46405     onLoad : function(){
46406         this.scrollToTop();
46407     },
46408
46409     /**
46410      * Scrolls the grid to the top
46411      */
46412     scrollToTop : function(){
46413         if(this.scroller){
46414             this.scroller.dom.scrollTop = 0;
46415             this.syncScroll();
46416         }
46417     },
46418
46419     /**
46420      * Gets a panel in the header of the grid that can be used for toolbars etc.
46421      * After modifying the contents of this panel a call to grid.autoSize() may be
46422      * required to register any changes in size.
46423      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
46424      * @return Roo.Element
46425      */
46426     getHeaderPanel : function(doShow){
46427         if(doShow){
46428             this.headerPanel.show();
46429         }
46430         return this.headerPanel;
46431         },
46432
46433         /**
46434      * Gets a panel in the footer of the grid that can be used for toolbars etc.
46435      * After modifying the contents of this panel a call to grid.autoSize() may be
46436      * required to register any changes in size.
46437      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
46438      * @return Roo.Element
46439      */
46440     getFooterPanel : function(doShow){
46441         if(doShow){
46442             this.footerPanel.show();
46443         }
46444         return this.footerPanel;
46445         },
46446
46447         initElements : function(){
46448             var E = Roo.Element;
46449             var el = this.grid.getGridEl().dom.firstChild;
46450             var cs = el.childNodes;
46451
46452             this.el = new E(el);
46453             this.headerPanel = new E(el.firstChild);
46454             this.headerPanel.enableDisplayMode("block");
46455
46456         this.scroller = new E(cs[1]);
46457             this.scrollSizer = new E(this.scroller.dom.firstChild);
46458
46459             this.lockedWrap = new E(cs[2]);
46460             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
46461             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
46462
46463             this.mainWrap = new E(cs[3]);
46464             this.mainHd = new E(this.mainWrap.dom.firstChild);
46465             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
46466
46467             this.footerPanel = new E(cs[4]);
46468             this.footerPanel.enableDisplayMode("block");
46469
46470         this.focusEl = new E(cs[5]);
46471         this.focusEl.swallowEvent("click", true);
46472         this.resizeProxy = new E(cs[6]);
46473
46474             this.headerSelector = String.format(
46475                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
46476                this.lockedHd.id, this.mainHd.id
46477             );
46478
46479             this.splitterSelector = String.format(
46480                '#{0} div.x-grid-split, #{1} div.x-grid-split',
46481                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
46482             );
46483     },
46484     idToCssName : function(s)
46485     {
46486         return s.replace(/[^a-z0-9]+/ig, '-');
46487     },
46488
46489         getHeaderCell : function(index){
46490             return Roo.DomQuery.select(this.headerSelector)[index];
46491         },
46492
46493         getHeaderCellMeasure : function(index){
46494             return this.getHeaderCell(index).firstChild;
46495         },
46496
46497         getHeaderCellText : function(index){
46498             return this.getHeaderCell(index).firstChild.firstChild;
46499         },
46500
46501         getLockedTable : function(){
46502             return this.lockedBody.dom.firstChild;
46503         },
46504
46505         getBodyTable : function(){
46506             return this.mainBody.dom.firstChild;
46507         },
46508
46509         getLockedRow : function(index){
46510             return this.getLockedTable().rows[index];
46511         },
46512
46513         getRow : function(index){
46514             return this.getBodyTable().rows[index];
46515         },
46516
46517         getRowComposite : function(index){
46518             if(!this.rowEl){
46519                 this.rowEl = new Roo.CompositeElementLite();
46520             }
46521         var els = [], lrow, mrow;
46522         if(lrow = this.getLockedRow(index)){
46523             els.push(lrow);
46524         }
46525         if(mrow = this.getRow(index)){
46526             els.push(mrow);
46527         }
46528         this.rowEl.elements = els;
46529             return this.rowEl;
46530         },
46531
46532         getCell : function(rowIndex, colIndex){
46533             var locked = this.cm.getLockedCount();
46534             var source;
46535             if(colIndex < locked){
46536                 source = this.lockedBody.dom.firstChild;
46537             }else{
46538                 source = this.mainBody.dom.firstChild;
46539                 colIndex -= locked;
46540             }
46541         return source.rows[rowIndex].childNodes[colIndex];
46542         },
46543
46544         getCellText : function(rowIndex, colIndex){
46545             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
46546         },
46547
46548         getCellBox : function(cell){
46549             var b = this.fly(cell).getBox();
46550         if(Roo.isOpera){ // opera fails to report the Y
46551             b.y = cell.offsetTop + this.mainBody.getY();
46552         }
46553         return b;
46554     },
46555
46556     getCellIndex : function(cell){
46557         var id = String(cell.className).match(this.cellRE);
46558         if(id){
46559             return parseInt(id[1], 10);
46560         }
46561         return 0;
46562     },
46563
46564     findHeaderIndex : function(n){
46565         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46566         return r ? this.getCellIndex(r) : false;
46567     },
46568
46569     findHeaderCell : function(n){
46570         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46571         return r ? r : false;
46572     },
46573
46574     findRowIndex : function(n){
46575         if(!n){
46576             return false;
46577         }
46578         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
46579         return r ? r.rowIndex : false;
46580     },
46581
46582     findCellIndex : function(node){
46583         var stop = this.el.dom;
46584         while(node && node != stop){
46585             if(this.findRE.test(node.className)){
46586                 return this.getCellIndex(node);
46587             }
46588             node = node.parentNode;
46589         }
46590         return false;
46591     },
46592
46593     getColumnId : function(index){
46594             return this.cm.getColumnId(index);
46595         },
46596
46597         getSplitters : function(){
46598             if(this.splitterSelector){
46599                return Roo.DomQuery.select(this.splitterSelector);
46600             }else{
46601                 return null;
46602             }
46603         },
46604
46605         getSplitter : function(index){
46606             return this.getSplitters()[index];
46607         },
46608
46609     onRowOver : function(e, t){
46610         var row;
46611         if((row = this.findRowIndex(t)) !== false){
46612             this.getRowComposite(row).addClass("x-grid-row-over");
46613         }
46614     },
46615
46616     onRowOut : function(e, t){
46617         var row;
46618         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
46619             this.getRowComposite(row).removeClass("x-grid-row-over");
46620         }
46621     },
46622
46623     renderHeaders : function(){
46624             var cm = this.cm;
46625         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
46626         var cb = [], lb = [], sb = [], lsb = [], p = {};
46627         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46628             p.cellId = "x-grid-hd-0-" + i;
46629             p.splitId = "x-grid-csplit-0-" + i;
46630             p.id = cm.getColumnId(i);
46631             p.title = cm.getColumnTooltip(i) || "";
46632             p.value = cm.getColumnHeader(i) || "";
46633             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
46634             if(!cm.isLocked(i)){
46635                 cb[cb.length] = ct.apply(p);
46636                 sb[sb.length] = st.apply(p);
46637             }else{
46638                 lb[lb.length] = ct.apply(p);
46639                 lsb[lsb.length] = st.apply(p);
46640             }
46641         }
46642         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
46643                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
46644         },
46645
46646         updateHeaders : function(){
46647         var html = this.renderHeaders();
46648         this.lockedHd.update(html[0]);
46649         this.mainHd.update(html[1]);
46650     },
46651
46652     /**
46653      * Focuses the specified row.
46654      * @param {Number} row The row index
46655      */
46656     focusRow : function(row){
46657         var x = this.scroller.dom.scrollLeft;
46658         this.focusCell(row, 0, false);
46659         this.scroller.dom.scrollLeft = x;
46660     },
46661
46662     /**
46663      * Focuses the specified cell.
46664      * @param {Number} row The row index
46665      * @param {Number} col The column index
46666      * @param {Boolean} hscroll false to disable horizontal scrolling
46667      */
46668     focusCell : function(row, col, hscroll){
46669         var el = this.ensureVisible(row, col, hscroll);
46670         this.focusEl.alignTo(el, "tl-tl");
46671         if(Roo.isGecko){
46672             this.focusEl.focus();
46673         }else{
46674             this.focusEl.focus.defer(1, this.focusEl);
46675         }
46676     },
46677
46678     /**
46679      * Scrolls the specified cell into view
46680      * @param {Number} row The row index
46681      * @param {Number} col The column index
46682      * @param {Boolean} hscroll false to disable horizontal scrolling
46683      */
46684     ensureVisible : function(row, col, hscroll){
46685         if(typeof row != "number"){
46686             row = row.rowIndex;
46687         }
46688         if(row < 0 && row >= this.ds.getCount()){
46689             return;
46690         }
46691         col = (col !== undefined ? col : 0);
46692         var cm = this.grid.colModel;
46693         while(cm.isHidden(col)){
46694             col++;
46695         }
46696
46697         var el = this.getCell(row, col);
46698         if(!el){
46699             return;
46700         }
46701         var c = this.scroller.dom;
46702
46703         var ctop = parseInt(el.offsetTop, 10);
46704         var cleft = parseInt(el.offsetLeft, 10);
46705         var cbot = ctop + el.offsetHeight;
46706         var cright = cleft + el.offsetWidth;
46707
46708         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
46709         var stop = parseInt(c.scrollTop, 10);
46710         var sleft = parseInt(c.scrollLeft, 10);
46711         var sbot = stop + ch;
46712         var sright = sleft + c.clientWidth;
46713
46714         if(ctop < stop){
46715                 c.scrollTop = ctop;
46716         }else if(cbot > sbot){
46717             c.scrollTop = cbot-ch;
46718         }
46719
46720         if(hscroll !== false){
46721             if(cleft < sleft){
46722                 c.scrollLeft = cleft;
46723             }else if(cright > sright){
46724                 c.scrollLeft = cright-c.clientWidth;
46725             }
46726         }
46727         return el;
46728     },
46729
46730     updateColumns : function(){
46731         this.grid.stopEditing();
46732         var cm = this.grid.colModel, colIds = this.getColumnIds();
46733         //var totalWidth = cm.getTotalWidth();
46734         var pos = 0;
46735         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46736             //if(cm.isHidden(i)) continue;
46737             var w = cm.getColumnWidth(i);
46738             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46739             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46740         }
46741         this.updateSplitters();
46742     },
46743
46744     generateRules : function(cm){
46745         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
46746         Roo.util.CSS.removeStyleSheet(rulesId);
46747         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46748             var cid = cm.getColumnId(i);
46749             var align = '';
46750             if(cm.config[i].align){
46751                 align = 'text-align:'+cm.config[i].align+';';
46752             }
46753             var hidden = '';
46754             if(cm.isHidden(i)){
46755                 hidden = 'display:none;';
46756             }
46757             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
46758             ruleBuf.push(
46759                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
46760                     this.hdSelector, cid, " {\n", align, width, "}\n",
46761                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
46762                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
46763         }
46764         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46765     },
46766
46767     updateSplitters : function(){
46768         var cm = this.cm, s = this.getSplitters();
46769         if(s){ // splitters not created yet
46770             var pos = 0, locked = true;
46771             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46772                 if(cm.isHidden(i)) continue;
46773                 var w = cm.getColumnWidth(i);
46774                 if(!cm.isLocked(i) && locked){
46775                     pos = 0;
46776                     locked = false;
46777                 }
46778                 pos += w;
46779                 s[i].style.left = (pos-this.splitOffset) + "px";
46780             }
46781         }
46782     },
46783
46784     handleHiddenChange : function(colModel, colIndex, hidden){
46785         if(hidden){
46786             this.hideColumn(colIndex);
46787         }else{
46788             this.unhideColumn(colIndex);
46789         }
46790     },
46791
46792     hideColumn : function(colIndex){
46793         var cid = this.getColumnId(colIndex);
46794         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
46795         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
46796         if(Roo.isSafari){
46797             this.updateHeaders();
46798         }
46799         this.updateSplitters();
46800         this.layout();
46801     },
46802
46803     unhideColumn : function(colIndex){
46804         var cid = this.getColumnId(colIndex);
46805         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
46806         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
46807
46808         if(Roo.isSafari){
46809             this.updateHeaders();
46810         }
46811         this.updateSplitters();
46812         this.layout();
46813     },
46814
46815     insertRows : function(dm, firstRow, lastRow, isUpdate){
46816         if(firstRow == 0 && lastRow == dm.getCount()-1){
46817             this.refresh();
46818         }else{
46819             if(!isUpdate){
46820                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
46821             }
46822             var s = this.getScrollState();
46823             var markup = this.renderRows(firstRow, lastRow);
46824             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
46825             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
46826             this.restoreScroll(s);
46827             if(!isUpdate){
46828                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
46829                 this.syncRowHeights(firstRow, lastRow);
46830                 this.stripeRows(firstRow);
46831                 this.layout();
46832             }
46833         }
46834     },
46835
46836     bufferRows : function(markup, target, index){
46837         var before = null, trows = target.rows, tbody = target.tBodies[0];
46838         if(index < trows.length){
46839             before = trows[index];
46840         }
46841         var b = document.createElement("div");
46842         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
46843         var rows = b.firstChild.rows;
46844         for(var i = 0, len = rows.length; i < len; i++){
46845             if(before){
46846                 tbody.insertBefore(rows[0], before);
46847             }else{
46848                 tbody.appendChild(rows[0]);
46849             }
46850         }
46851         b.innerHTML = "";
46852         b = null;
46853     },
46854
46855     deleteRows : function(dm, firstRow, lastRow){
46856         if(dm.getRowCount()<1){
46857             this.fireEvent("beforerefresh", this);
46858             this.mainBody.update("");
46859             this.lockedBody.update("");
46860             this.fireEvent("refresh", this);
46861         }else{
46862             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
46863             var bt = this.getBodyTable();
46864             var tbody = bt.firstChild;
46865             var rows = bt.rows;
46866             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
46867                 tbody.removeChild(rows[firstRow]);
46868             }
46869             this.stripeRows(firstRow);
46870             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
46871         }
46872     },
46873
46874     updateRows : function(dataSource, firstRow, lastRow){
46875         var s = this.getScrollState();
46876         this.refresh();
46877         this.restoreScroll(s);
46878     },
46879
46880     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
46881         if(!noRefresh){
46882            this.refresh();
46883         }
46884         this.updateHeaderSortState();
46885     },
46886
46887     getScrollState : function(){
46888         var sb = this.scroller.dom;
46889         return {left: sb.scrollLeft, top: sb.scrollTop};
46890     },
46891
46892     stripeRows : function(startRow){
46893         if(!this.grid.stripeRows || this.ds.getCount() < 1){
46894             return;
46895         }
46896         startRow = startRow || 0;
46897         var rows = this.getBodyTable().rows;
46898         var lrows = this.getLockedTable().rows;
46899         var cls = ' x-grid-row-alt ';
46900         for(var i = startRow, len = rows.length; i < len; i++){
46901             var row = rows[i], lrow = lrows[i];
46902             var isAlt = ((i+1) % 2 == 0);
46903             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
46904             if(isAlt == hasAlt){
46905                 continue;
46906             }
46907             if(isAlt){
46908                 row.className += " x-grid-row-alt";
46909             }else{
46910                 row.className = row.className.replace("x-grid-row-alt", "");
46911             }
46912             if(lrow){
46913                 lrow.className = row.className;
46914             }
46915         }
46916     },
46917
46918     restoreScroll : function(state){
46919         var sb = this.scroller.dom;
46920         sb.scrollLeft = state.left;
46921         sb.scrollTop = state.top;
46922         this.syncScroll();
46923     },
46924
46925     syncScroll : function(){
46926         var sb = this.scroller.dom;
46927         var sh = this.mainHd.dom;
46928         var bs = this.mainBody.dom;
46929         var lv = this.lockedBody.dom;
46930         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
46931         lv.scrollTop = bs.scrollTop = sb.scrollTop;
46932     },
46933
46934     handleScroll : function(e){
46935         this.syncScroll();
46936         var sb = this.scroller.dom;
46937         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
46938         e.stopEvent();
46939     },
46940
46941     handleWheel : function(e){
46942         var d = e.getWheelDelta();
46943         this.scroller.dom.scrollTop -= d*22;
46944         // set this here to prevent jumpy scrolling on large tables
46945         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
46946         e.stopEvent();
46947     },
46948
46949     renderRows : function(startRow, endRow){
46950         // pull in all the crap needed to render rows
46951         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
46952         var colCount = cm.getColumnCount();
46953
46954         if(ds.getCount() < 1){
46955             return ["", ""];
46956         }
46957
46958         // build a map for all the columns
46959         var cs = [];
46960         for(var i = 0; i < colCount; i++){
46961             var name = cm.getDataIndex(i);
46962             cs[i] = {
46963                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
46964                 renderer : cm.getRenderer(i),
46965                 id : cm.getColumnId(i),
46966                 locked : cm.isLocked(i)
46967             };
46968         }
46969
46970         startRow = startRow || 0;
46971         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
46972
46973         // records to render
46974         var rs = ds.getRange(startRow, endRow);
46975
46976         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
46977     },
46978
46979     // As much as I hate to duplicate code, this was branched because FireFox really hates
46980     // [].join("") on strings. The performance difference was substantial enough to
46981     // branch this function
46982     doRender : Roo.isGecko ?
46983             function(cs, rs, ds, startRow, colCount, stripe){
46984                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46985                 // buffers
46986                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46987                 for(var j = 0, len = rs.length; j < len; j++){
46988                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
46989                     for(var i = 0; i < colCount; i++){
46990                         c = cs[i];
46991                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46992                         p.id = c.id;
46993                         p.css = p.attr = "";
46994                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46995                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46996                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
46997                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
46998                         }
46999                         var markup = ct.apply(p);
47000                         if(!c.locked){
47001                             cb+= markup;
47002                         }else{
47003                             lcb+= markup;
47004                         }
47005                     }
47006                     var alt = [];
47007                     if(stripe && ((rowIndex+1) % 2 == 0)){
47008                         alt[0] = "x-grid-row-alt";
47009                     }
47010                     if(r.dirty){
47011                         alt[1] = " x-grid-dirty-row";
47012                     }
47013                     rp.cells = lcb;
47014                     if(this.getRowClass){
47015                         alt[2] = this.getRowClass(r, rowIndex);
47016                     }
47017                     rp.alt = alt.join(" ");
47018                     lbuf+= rt.apply(rp);
47019                     rp.cells = cb;
47020                     buf+=  rt.apply(rp);
47021                 }
47022                 return [lbuf, buf];
47023             } :
47024             function(cs, rs, ds, startRow, colCount, stripe){
47025                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47026                 // buffers
47027                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47028                 for(var j = 0, len = rs.length; j < len; j++){
47029                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47030                     for(var i = 0; i < colCount; i++){
47031                         c = cs[i];
47032                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47033                         p.id = c.id;
47034                         p.css = p.attr = "";
47035                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47036                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47037                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47038                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47039                         }
47040                         var markup = ct.apply(p);
47041                         if(!c.locked){
47042                             cb[cb.length] = markup;
47043                         }else{
47044                             lcb[lcb.length] = markup;
47045                         }
47046                     }
47047                     var alt = [];
47048                     if(stripe && ((rowIndex+1) % 2 == 0)){
47049                         alt[0] = "x-grid-row-alt";
47050                     }
47051                     if(r.dirty){
47052                         alt[1] = " x-grid-dirty-row";
47053                     }
47054                     rp.cells = lcb;
47055                     if(this.getRowClass){
47056                         alt[2] = this.getRowClass(r, rowIndex);
47057                     }
47058                     rp.alt = alt.join(" ");
47059                     rp.cells = lcb.join("");
47060                     lbuf[lbuf.length] = rt.apply(rp);
47061                     rp.cells = cb.join("");
47062                     buf[buf.length] =  rt.apply(rp);
47063                 }
47064                 return [lbuf.join(""), buf.join("")];
47065             },
47066
47067     renderBody : function(){
47068         var markup = this.renderRows();
47069         var bt = this.templates.body;
47070         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
47071     },
47072
47073     /**
47074      * Refreshes the grid
47075      * @param {Boolean} headersToo
47076      */
47077     refresh : function(headersToo){
47078         this.fireEvent("beforerefresh", this);
47079         this.grid.stopEditing();
47080         var result = this.renderBody();
47081         this.lockedBody.update(result[0]);
47082         this.mainBody.update(result[1]);
47083         if(headersToo === true){
47084             this.updateHeaders();
47085             this.updateColumns();
47086             this.updateSplitters();
47087             this.updateHeaderSortState();
47088         }
47089         this.syncRowHeights();
47090         this.layout();
47091         this.fireEvent("refresh", this);
47092     },
47093
47094     handleColumnMove : function(cm, oldIndex, newIndex){
47095         this.indexMap = null;
47096         var s = this.getScrollState();
47097         this.refresh(true);
47098         this.restoreScroll(s);
47099         this.afterMove(newIndex);
47100     },
47101
47102     afterMove : function(colIndex){
47103         if(this.enableMoveAnim && Roo.enableFx){
47104             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
47105         }
47106     },
47107
47108     updateCell : function(dm, rowIndex, dataIndex){
47109         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
47110         if(typeof colIndex == "undefined"){ // not present in grid
47111             return;
47112         }
47113         var cm = this.grid.colModel;
47114         var cell = this.getCell(rowIndex, colIndex);
47115         var cellText = this.getCellText(rowIndex, colIndex);
47116
47117         var p = {
47118             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
47119             id : cm.getColumnId(colIndex),
47120             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
47121         };
47122         var renderer = cm.getRenderer(colIndex);
47123         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
47124         if(typeof val == "undefined" || val === "") val = "&#160;";
47125         cellText.innerHTML = val;
47126         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
47127         this.syncRowHeights(rowIndex, rowIndex);
47128     },
47129
47130     calcColumnWidth : function(colIndex, maxRowsToMeasure){
47131         var maxWidth = 0;
47132         if(this.grid.autoSizeHeaders){
47133             var h = this.getHeaderCellMeasure(colIndex);
47134             maxWidth = Math.max(maxWidth, h.scrollWidth);
47135         }
47136         var tb, index;
47137         if(this.cm.isLocked(colIndex)){
47138             tb = this.getLockedTable();
47139             index = colIndex;
47140         }else{
47141             tb = this.getBodyTable();
47142             index = colIndex - this.cm.getLockedCount();
47143         }
47144         if(tb && tb.rows){
47145             var rows = tb.rows;
47146             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
47147             for(var i = 0; i < stopIndex; i++){
47148                 var cell = rows[i].childNodes[index].firstChild;
47149                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
47150             }
47151         }
47152         return maxWidth + /*margin for error in IE*/ 5;
47153     },
47154     /**
47155      * Autofit a column to its content.
47156      * @param {Number} colIndex
47157      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
47158      */
47159      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
47160          if(this.cm.isHidden(colIndex)){
47161              return; // can't calc a hidden column
47162          }
47163         if(forceMinSize){
47164             var cid = this.cm.getColumnId(colIndex);
47165             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
47166            if(this.grid.autoSizeHeaders){
47167                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
47168            }
47169         }
47170         var newWidth = this.calcColumnWidth(colIndex);
47171         this.cm.setColumnWidth(colIndex,
47172             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
47173         if(!suppressEvent){
47174             this.grid.fireEvent("columnresize", colIndex, newWidth);
47175         }
47176     },
47177
47178     /**
47179      * Autofits all columns to their content and then expands to fit any extra space in the grid
47180      */
47181      autoSizeColumns : function(){
47182         var cm = this.grid.colModel;
47183         var colCount = cm.getColumnCount();
47184         for(var i = 0; i < colCount; i++){
47185             this.autoSizeColumn(i, true, true);
47186         }
47187         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
47188             this.fitColumns();
47189         }else{
47190             this.updateColumns();
47191             this.layout();
47192         }
47193     },
47194
47195     /**
47196      * Autofits all columns to the grid's width proportionate with their current size
47197      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
47198      */
47199     fitColumns : function(reserveScrollSpace){
47200         var cm = this.grid.colModel;
47201         var colCount = cm.getColumnCount();
47202         var cols = [];
47203         var width = 0;
47204         var i, w;
47205         for (i = 0; i < colCount; i++){
47206             if(!cm.isHidden(i) && !cm.isFixed(i)){
47207                 w = cm.getColumnWidth(i);
47208                 cols.push(i);
47209                 cols.push(w);
47210                 width += w;
47211             }
47212         }
47213         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
47214         if(reserveScrollSpace){
47215             avail -= 17;
47216         }
47217         var frac = (avail - cm.getTotalWidth())/width;
47218         while (cols.length){
47219             w = cols.pop();
47220             i = cols.pop();
47221             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
47222         }
47223         this.updateColumns();
47224         this.layout();
47225     },
47226
47227     onRowSelect : function(rowIndex){
47228         var row = this.getRowComposite(rowIndex);
47229         row.addClass("x-grid-row-selected");
47230     },
47231
47232     onRowDeselect : function(rowIndex){
47233         var row = this.getRowComposite(rowIndex);
47234         row.removeClass("x-grid-row-selected");
47235     },
47236
47237     onCellSelect : function(row, col){
47238         var cell = this.getCell(row, col);
47239         if(cell){
47240             Roo.fly(cell).addClass("x-grid-cell-selected");
47241         }
47242     },
47243
47244     onCellDeselect : function(row, col){
47245         var cell = this.getCell(row, col);
47246         if(cell){
47247             Roo.fly(cell).removeClass("x-grid-cell-selected");
47248         }
47249     },
47250
47251     updateHeaderSortState : function(){
47252         var state = this.ds.getSortState();
47253         if(!state){
47254             return;
47255         }
47256         this.sortState = state;
47257         var sortColumn = this.cm.findColumnIndex(state.field);
47258         if(sortColumn != -1){
47259             var sortDir = state.direction;
47260             var sc = this.sortClasses;
47261             var hds = this.el.select(this.headerSelector).removeClass(sc);
47262             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47263         }
47264     },
47265
47266     handleHeaderClick : function(g, index){
47267         if(this.headersDisabled){
47268             return;
47269         }
47270         var dm = g.dataSource, cm = g.colModel;
47271             if(!cm.isSortable(index)){
47272             return;
47273         }
47274             g.stopEditing();
47275         dm.sort(cm.getDataIndex(index));
47276     },
47277
47278
47279     destroy : function(){
47280         if(this.colMenu){
47281             this.colMenu.removeAll();
47282             Roo.menu.MenuMgr.unregister(this.colMenu);
47283             this.colMenu.getEl().remove();
47284             delete this.colMenu;
47285         }
47286         if(this.hmenu){
47287             this.hmenu.removeAll();
47288             Roo.menu.MenuMgr.unregister(this.hmenu);
47289             this.hmenu.getEl().remove();
47290             delete this.hmenu;
47291         }
47292         if(this.grid.enableColumnMove){
47293             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47294             if(dds){
47295                 for(var dd in dds){
47296                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
47297                         var elid = dds[dd].dragElId;
47298                         dds[dd].unreg();
47299                         Roo.get(elid).remove();
47300                     } else if(dds[dd].config.isTarget){
47301                         dds[dd].proxyTop.remove();
47302                         dds[dd].proxyBottom.remove();
47303                         dds[dd].unreg();
47304                     }
47305                     if(Roo.dd.DDM.locationCache[dd]){
47306                         delete Roo.dd.DDM.locationCache[dd];
47307                     }
47308                 }
47309                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47310             }
47311         }
47312         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
47313         this.bind(null, null);
47314         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
47315     },
47316
47317     handleLockChange : function(){
47318         this.refresh(true);
47319     },
47320
47321     onDenyColumnLock : function(){
47322
47323     },
47324
47325     onDenyColumnHide : function(){
47326
47327     },
47328
47329     handleHdMenuClick : function(item){
47330         var index = this.hdCtxIndex;
47331         var cm = this.cm, ds = this.ds;
47332         switch(item.id){
47333             case "asc":
47334                 ds.sort(cm.getDataIndex(index), "ASC");
47335                 break;
47336             case "desc":
47337                 ds.sort(cm.getDataIndex(index), "DESC");
47338                 break;
47339             case "lock":
47340                 var lc = cm.getLockedCount();
47341                 if(cm.getColumnCount(true) <= lc+1){
47342                     this.onDenyColumnLock();
47343                     return;
47344                 }
47345                 if(lc != index){
47346                     cm.setLocked(index, true, true);
47347                     cm.moveColumn(index, lc);
47348                     this.grid.fireEvent("columnmove", index, lc);
47349                 }else{
47350                     cm.setLocked(index, true);
47351                 }
47352             break;
47353             case "unlock":
47354                 var lc = cm.getLockedCount();
47355                 if((lc-1) != index){
47356                     cm.setLocked(index, false, true);
47357                     cm.moveColumn(index, lc-1);
47358                     this.grid.fireEvent("columnmove", index, lc-1);
47359                 }else{
47360                     cm.setLocked(index, false);
47361                 }
47362             break;
47363             default:
47364                 index = cm.getIndexById(item.id.substr(4));
47365                 if(index != -1){
47366                     if(item.checked && cm.getColumnCount(true) <= 1){
47367                         this.onDenyColumnHide();
47368                         return false;
47369                     }
47370                     cm.setHidden(index, item.checked);
47371                 }
47372         }
47373         return true;
47374     },
47375
47376     beforeColMenuShow : function(){
47377         var cm = this.cm,  colCount = cm.getColumnCount();
47378         this.colMenu.removeAll();
47379         for(var i = 0; i < colCount; i++){
47380             this.colMenu.add(new Roo.menu.CheckItem({
47381                 id: "col-"+cm.getColumnId(i),
47382                 text: cm.getColumnHeader(i),
47383                 checked: !cm.isHidden(i),
47384                 hideOnClick:false
47385             }));
47386         }
47387     },
47388
47389     handleHdCtx : function(g, index, e){
47390         e.stopEvent();
47391         var hd = this.getHeaderCell(index);
47392         this.hdCtxIndex = index;
47393         var ms = this.hmenu.items, cm = this.cm;
47394         ms.get("asc").setDisabled(!cm.isSortable(index));
47395         ms.get("desc").setDisabled(!cm.isSortable(index));
47396         if(this.grid.enableColLock !== false){
47397             ms.get("lock").setDisabled(cm.isLocked(index));
47398             ms.get("unlock").setDisabled(!cm.isLocked(index));
47399         }
47400         this.hmenu.show(hd, "tl-bl");
47401     },
47402
47403     handleHdOver : function(e){
47404         var hd = this.findHeaderCell(e.getTarget());
47405         if(hd && !this.headersDisabled){
47406             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
47407                this.fly(hd).addClass("x-grid-hd-over");
47408             }
47409         }
47410     },
47411
47412     handleHdOut : function(e){
47413         var hd = this.findHeaderCell(e.getTarget());
47414         if(hd){
47415             this.fly(hd).removeClass("x-grid-hd-over");
47416         }
47417     },
47418
47419     handleSplitDblClick : function(e, t){
47420         var i = this.getCellIndex(t);
47421         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
47422             this.autoSizeColumn(i, true);
47423             this.layout();
47424         }
47425     },
47426
47427     render : function(){
47428
47429         var cm = this.cm;
47430         var colCount = cm.getColumnCount();
47431
47432         if(this.grid.monitorWindowResize === true){
47433             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47434         }
47435         var header = this.renderHeaders();
47436         var body = this.templates.body.apply({rows:""});
47437         var html = this.templates.master.apply({
47438             lockedBody: body,
47439             body: body,
47440             lockedHeader: header[0],
47441             header: header[1]
47442         });
47443
47444         //this.updateColumns();
47445
47446         this.grid.getGridEl().dom.innerHTML = html;
47447
47448         this.initElements();
47449
47450         this.scroller.on("scroll", this.handleScroll, this);
47451         this.lockedBody.on("mousewheel", this.handleWheel, this);
47452         this.mainBody.on("mousewheel", this.handleWheel, this);
47453
47454         this.mainHd.on("mouseover", this.handleHdOver, this);
47455         this.mainHd.on("mouseout", this.handleHdOut, this);
47456         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
47457                 {delegate: "."+this.splitClass});
47458
47459         this.lockedHd.on("mouseover", this.handleHdOver, this);
47460         this.lockedHd.on("mouseout", this.handleHdOut, this);
47461         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
47462                 {delegate: "."+this.splitClass});
47463
47464         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
47465             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47466         }
47467
47468         this.updateSplitters();
47469
47470         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
47471             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47472             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47473         }
47474
47475         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
47476             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
47477             this.hmenu.add(
47478                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
47479                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
47480             );
47481             if(this.grid.enableColLock !== false){
47482                 this.hmenu.add('-',
47483                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
47484                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
47485                 );
47486             }
47487             if(this.grid.enableColumnHide !== false){
47488
47489                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
47490                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
47491                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
47492
47493                 this.hmenu.add('-',
47494                     {id:"columns", text: this.columnsText, menu: this.colMenu}
47495                 );
47496             }
47497             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
47498
47499             this.grid.on("headercontextmenu", this.handleHdCtx, this);
47500         }
47501
47502         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
47503             this.dd = new Roo.grid.GridDragZone(this.grid, {
47504                 ddGroup : this.grid.ddGroup || 'GridDD'
47505             });
47506         }
47507
47508         /*
47509         for(var i = 0; i < colCount; i++){
47510             if(cm.isHidden(i)){
47511                 this.hideColumn(i);
47512             }
47513             if(cm.config[i].align){
47514                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
47515                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
47516             }
47517         }*/
47518         
47519         this.updateHeaderSortState();
47520
47521         this.beforeInitialResize();
47522         this.layout(true);
47523
47524         // two part rendering gives faster view to the user
47525         this.renderPhase2.defer(1, this);
47526     },
47527
47528     renderPhase2 : function(){
47529         // render the rows now
47530         this.refresh();
47531         if(this.grid.autoSizeColumns){
47532             this.autoSizeColumns();
47533         }
47534     },
47535
47536     beforeInitialResize : function(){
47537
47538     },
47539
47540     onColumnSplitterMoved : function(i, w){
47541         this.userResized = true;
47542         var cm = this.grid.colModel;
47543         cm.setColumnWidth(i, w, true);
47544         var cid = cm.getColumnId(i);
47545         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47546         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47547         this.updateSplitters();
47548         this.layout();
47549         this.grid.fireEvent("columnresize", i, w);
47550     },
47551
47552     syncRowHeights : function(startIndex, endIndex){
47553         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
47554             startIndex = startIndex || 0;
47555             var mrows = this.getBodyTable().rows;
47556             var lrows = this.getLockedTable().rows;
47557             var len = mrows.length-1;
47558             endIndex = Math.min(endIndex || len, len);
47559             for(var i = startIndex; i <= endIndex; i++){
47560                 var m = mrows[i], l = lrows[i];
47561                 var h = Math.max(m.offsetHeight, l.offsetHeight);
47562                 m.style.height = l.style.height = h + "px";
47563             }
47564         }
47565     },
47566
47567     layout : function(initialRender, is2ndPass){
47568         var g = this.grid;
47569         var auto = g.autoHeight;
47570         var scrollOffset = 16;
47571         var c = g.getGridEl(), cm = this.cm,
47572                 expandCol = g.autoExpandColumn,
47573                 gv = this;
47574         //c.beginMeasure();
47575
47576         if(!c.dom.offsetWidth){ // display:none?
47577             if(initialRender){
47578                 this.lockedWrap.show();
47579                 this.mainWrap.show();
47580             }
47581             return;
47582         }
47583
47584         var hasLock = this.cm.isLocked(0);
47585
47586         var tbh = this.headerPanel.getHeight();
47587         var bbh = this.footerPanel.getHeight();
47588
47589         if(auto){
47590             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
47591             var newHeight = ch + c.getBorderWidth("tb");
47592             if(g.maxHeight){
47593                 newHeight = Math.min(g.maxHeight, newHeight);
47594             }
47595             c.setHeight(newHeight);
47596         }
47597
47598         if(g.autoWidth){
47599             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
47600         }
47601
47602         var s = this.scroller;
47603
47604         var csize = c.getSize(true);
47605
47606         this.el.setSize(csize.width, csize.height);
47607
47608         this.headerPanel.setWidth(csize.width);
47609         this.footerPanel.setWidth(csize.width);
47610
47611         var hdHeight = this.mainHd.getHeight();
47612         var vw = csize.width;
47613         var vh = csize.height - (tbh + bbh);
47614
47615         s.setSize(vw, vh);
47616
47617         var bt = this.getBodyTable();
47618         var ltWidth = hasLock ?
47619                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
47620
47621         var scrollHeight = bt.offsetHeight;
47622         var scrollWidth = ltWidth + bt.offsetWidth;
47623         var vscroll = false, hscroll = false;
47624
47625         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
47626
47627         var lw = this.lockedWrap, mw = this.mainWrap;
47628         var lb = this.lockedBody, mb = this.mainBody;
47629
47630         setTimeout(function(){
47631             var t = s.dom.offsetTop;
47632             var w = s.dom.clientWidth,
47633                 h = s.dom.clientHeight;
47634
47635             lw.setTop(t);
47636             lw.setSize(ltWidth, h);
47637
47638             mw.setLeftTop(ltWidth, t);
47639             mw.setSize(w-ltWidth, h);
47640
47641             lb.setHeight(h-hdHeight);
47642             mb.setHeight(h-hdHeight);
47643
47644             if(is2ndPass !== true && !gv.userResized && expandCol){
47645                 // high speed resize without full column calculation
47646                 
47647                 var ci = cm.getIndexById(expandCol);
47648                 if (ci < 0) {
47649                     ci = cm.findColumnIndex(expandCol);
47650                 }
47651                 ci = Math.max(0, ci); // make sure it's got at least the first col.
47652                 var expandId = cm.getColumnId(ci);
47653                 var  tw = cm.getTotalWidth(false);
47654                 var currentWidth = cm.getColumnWidth(ci);
47655                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
47656                 if(currentWidth != cw){
47657                     cm.setColumnWidth(ci, cw, true);
47658                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47659                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47660                     gv.updateSplitters();
47661                     gv.layout(false, true);
47662                 }
47663             }
47664
47665             if(initialRender){
47666                 lw.show();
47667                 mw.show();
47668             }
47669             //c.endMeasure();
47670         }, 10);
47671     },
47672
47673     onWindowResize : function(){
47674         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
47675             return;
47676         }
47677         this.layout();
47678     },
47679
47680     appendFooter : function(parentEl){
47681         return null;
47682     },
47683
47684     sortAscText : "Sort Ascending",
47685     sortDescText : "Sort Descending",
47686     lockText : "Lock Column",
47687     unlockText : "Unlock Column",
47688     columnsText : "Columns"
47689 });
47690
47691
47692 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
47693     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
47694     this.proxy.el.addClass('x-grid3-col-dd');
47695 };
47696
47697 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
47698     handleMouseDown : function(e){
47699
47700     },
47701
47702     callHandleMouseDown : function(e){
47703         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
47704     }
47705 });
47706 /*
47707  * Based on:
47708  * Ext JS Library 1.1.1
47709  * Copyright(c) 2006-2007, Ext JS, LLC.
47710  *
47711  * Originally Released Under LGPL - original licence link has changed is not relivant.
47712  *
47713  * Fork - LGPL
47714  * <script type="text/javascript">
47715  */
47716  
47717 // private
47718 // This is a support class used internally by the Grid components
47719 Roo.grid.SplitDragZone = function(grid, hd, hd2){
47720     this.grid = grid;
47721     this.view = grid.getView();
47722     this.proxy = this.view.resizeProxy;
47723     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
47724         "gridSplitters" + this.grid.getGridEl().id, {
47725         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
47726     });
47727     this.setHandleElId(Roo.id(hd));
47728     this.setOuterHandleElId(Roo.id(hd2));
47729     this.scroll = false;
47730 };
47731 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
47732     fly: Roo.Element.fly,
47733
47734     b4StartDrag : function(x, y){
47735         this.view.headersDisabled = true;
47736         this.proxy.setHeight(this.view.mainWrap.getHeight());
47737         var w = this.cm.getColumnWidth(this.cellIndex);
47738         var minw = Math.max(w-this.grid.minColumnWidth, 0);
47739         this.resetConstraints();
47740         this.setXConstraint(minw, 1000);
47741         this.setYConstraint(0, 0);
47742         this.minX = x - minw;
47743         this.maxX = x + 1000;
47744         this.startPos = x;
47745         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
47746     },
47747
47748
47749     handleMouseDown : function(e){
47750         ev = Roo.EventObject.setEvent(e);
47751         var t = this.fly(ev.getTarget());
47752         if(t.hasClass("x-grid-split")){
47753             this.cellIndex = this.view.getCellIndex(t.dom);
47754             this.split = t.dom;
47755             this.cm = this.grid.colModel;
47756             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
47757                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
47758             }
47759         }
47760     },
47761
47762     endDrag : function(e){
47763         this.view.headersDisabled = false;
47764         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
47765         var diff = endX - this.startPos;
47766         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
47767     },
47768
47769     autoOffset : function(){
47770         this.setDelta(0,0);
47771     }
47772 });/*
47773  * Based on:
47774  * Ext JS Library 1.1.1
47775  * Copyright(c) 2006-2007, Ext JS, LLC.
47776  *
47777  * Originally Released Under LGPL - original licence link has changed is not relivant.
47778  *
47779  * Fork - LGPL
47780  * <script type="text/javascript">
47781  */
47782  
47783 // private
47784 // This is a support class used internally by the Grid components
47785 Roo.grid.GridDragZone = function(grid, config){
47786     this.view = grid.getView();
47787     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
47788     if(this.view.lockedBody){
47789         this.setHandleElId(Roo.id(this.view.mainBody.dom));
47790         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
47791     }
47792     this.scroll = false;
47793     this.grid = grid;
47794     this.ddel = document.createElement('div');
47795     this.ddel.className = 'x-grid-dd-wrap';
47796 };
47797
47798 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
47799     ddGroup : "GridDD",
47800
47801     getDragData : function(e){
47802         var t = Roo.lib.Event.getTarget(e);
47803         var rowIndex = this.view.findRowIndex(t);
47804         if(rowIndex !== false){
47805             var sm = this.grid.selModel;
47806             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
47807               //  sm.mouseDown(e, t);
47808             //}
47809             if (e.hasModifier()){
47810                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
47811             }
47812             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
47813         }
47814         return false;
47815     },
47816
47817     onInitDrag : function(e){
47818         var data = this.dragData;
47819         this.ddel.innerHTML = this.grid.getDragDropText();
47820         this.proxy.update(this.ddel);
47821         // fire start drag?
47822     },
47823
47824     afterRepair : function(){
47825         this.dragging = false;
47826     },
47827
47828     getRepairXY : function(e, data){
47829         return false;
47830     },
47831
47832     onEndDrag : function(data, e){
47833         // fire end drag?
47834     },
47835
47836     onValidDrop : function(dd, e, id){
47837         // fire drag drop?
47838         this.hideProxy();
47839     },
47840
47841     beforeInvalidDrop : function(e, id){
47842
47843     }
47844 });/*
47845  * Based on:
47846  * Ext JS Library 1.1.1
47847  * Copyright(c) 2006-2007, Ext JS, LLC.
47848  *
47849  * Originally Released Under LGPL - original licence link has changed is not relivant.
47850  *
47851  * Fork - LGPL
47852  * <script type="text/javascript">
47853  */
47854  
47855
47856 /**
47857  * @class Roo.grid.ColumnModel
47858  * @extends Roo.util.Observable
47859  * This is the default implementation of a ColumnModel used by the Grid. It defines
47860  * the columns in the grid.
47861  * <br>Usage:<br>
47862  <pre><code>
47863  var colModel = new Roo.grid.ColumnModel([
47864         {header: "Ticker", width: 60, sortable: true, locked: true},
47865         {header: "Company Name", width: 150, sortable: true},
47866         {header: "Market Cap.", width: 100, sortable: true},
47867         {header: "$ Sales", width: 100, sortable: true, renderer: money},
47868         {header: "Employees", width: 100, sortable: true, resizable: false}
47869  ]);
47870  </code></pre>
47871  * <p>
47872  
47873  * The config options listed for this class are options which may appear in each
47874  * individual column definition.
47875  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
47876  * @constructor
47877  * @param {Object} config An Array of column config objects. See this class's
47878  * config objects for details.
47879 */
47880 Roo.grid.ColumnModel = function(config){
47881         /**
47882      * The config passed into the constructor
47883      */
47884     this.config = config;
47885     this.lookup = {};
47886
47887     // if no id, create one
47888     // if the column does not have a dataIndex mapping,
47889     // map it to the order it is in the config
47890     for(var i = 0, len = config.length; i < len; i++){
47891         var c = config[i];
47892         if(typeof c.dataIndex == "undefined"){
47893             c.dataIndex = i;
47894         }
47895         if(typeof c.renderer == "string"){
47896             c.renderer = Roo.util.Format[c.renderer];
47897         }
47898         if(typeof c.id == "undefined"){
47899             c.id = Roo.id();
47900         }
47901         if(c.editor && c.editor.xtype){
47902             c.editor  = Roo.factory(c.editor, Roo.grid);
47903         }
47904         if(c.editor && c.editor.isFormField){
47905             c.editor = new Roo.grid.GridEditor(c.editor);
47906         }
47907         this.lookup[c.id] = c;
47908     }
47909
47910     /**
47911      * The width of columns which have no width specified (defaults to 100)
47912      * @type Number
47913      */
47914     this.defaultWidth = 100;
47915
47916     /**
47917      * Default sortable of columns which have no sortable specified (defaults to false)
47918      * @type Boolean
47919      */
47920     this.defaultSortable = false;
47921
47922     this.addEvents({
47923         /**
47924              * @event widthchange
47925              * Fires when the width of a column changes.
47926              * @param {ColumnModel} this
47927              * @param {Number} columnIndex The column index
47928              * @param {Number} newWidth The new width
47929              */
47930             "widthchange": true,
47931         /**
47932              * @event headerchange
47933              * Fires when the text of a header changes.
47934              * @param {ColumnModel} this
47935              * @param {Number} columnIndex The column index
47936              * @param {Number} newText The new header text
47937              */
47938             "headerchange": true,
47939         /**
47940              * @event hiddenchange
47941              * Fires when a column is hidden or "unhidden".
47942              * @param {ColumnModel} this
47943              * @param {Number} columnIndex The column index
47944              * @param {Boolean} hidden true if hidden, false otherwise
47945              */
47946             "hiddenchange": true,
47947             /**
47948          * @event columnmoved
47949          * Fires when a column is moved.
47950          * @param {ColumnModel} this
47951          * @param {Number} oldIndex
47952          * @param {Number} newIndex
47953          */
47954         "columnmoved" : true,
47955         /**
47956          * @event columlockchange
47957          * Fires when a column's locked state is changed
47958          * @param {ColumnModel} this
47959          * @param {Number} colIndex
47960          * @param {Boolean} locked true if locked
47961          */
47962         "columnlockchange" : true
47963     });
47964     Roo.grid.ColumnModel.superclass.constructor.call(this);
47965 };
47966 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
47967     /**
47968      * @cfg {String} header The header text to display in the Grid view.
47969      */
47970     /**
47971      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
47972      * {@link Roo.data.Record} definition from which to draw the column's value. If not
47973      * specified, the column's index is used as an index into the Record's data Array.
47974      */
47975     /**
47976      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
47977      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
47978      */
47979     /**
47980      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
47981      * Defaults to the value of the {@link #defaultSortable} property.
47982      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
47983      */
47984     /**
47985      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
47986      */
47987     /**
47988      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
47989      */
47990     /**
47991      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
47992      */
47993     /**
47994      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
47995      */
47996     /**
47997      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
47998      * given the cell's data value. See {@link #setRenderer}. If not specified, the
47999      * default renderer uses the raw data value.
48000      */
48001        /**
48002      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48003      */
48004     /**
48005      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48006      */
48007
48008     /**
48009      * Returns the id of the column at the specified index.
48010      * @param {Number} index The column index
48011      * @return {String} the id
48012      */
48013     getColumnId : function(index){
48014         return this.config[index].id;
48015     },
48016
48017     /**
48018      * Returns the column for a specified id.
48019      * @param {String} id The column id
48020      * @return {Object} the column
48021      */
48022     getColumnById : function(id){
48023         return this.lookup[id];
48024     },
48025
48026     /**
48027      * Returns the index for a specified column id.
48028      * @param {String} id The column id
48029      * @return {Number} the index, or -1 if not found
48030      */
48031     getIndexById : function(id){
48032         for(var i = 0, len = this.config.length; i < len; i++){
48033             if(this.config[i].id == id){
48034                 return i;
48035             }
48036         }
48037         return -1;
48038     },
48039     /**
48040      * Returns the index for a specified column dataIndex.
48041      * @param {String} dataIndex The column dataIndex
48042      * @return {Number} the index, or -1 if not found
48043      */
48044     
48045     findColumnIndex : function(dataIndex){
48046         for(var i = 0, len = this.config.length; i < len; i++){
48047             if(this.config[i].dataIndex == dataIndex){
48048                 return i;
48049             }
48050         }
48051         return -1;
48052     },
48053     
48054     
48055     moveColumn : function(oldIndex, newIndex){
48056         var c = this.config[oldIndex];
48057         this.config.splice(oldIndex, 1);
48058         this.config.splice(newIndex, 0, c);
48059         this.dataMap = null;
48060         this.fireEvent("columnmoved", this, oldIndex, newIndex);
48061     },
48062
48063     isLocked : function(colIndex){
48064         return this.config[colIndex].locked === true;
48065     },
48066
48067     setLocked : function(colIndex, value, suppressEvent){
48068         if(this.isLocked(colIndex) == value){
48069             return;
48070         }
48071         this.config[colIndex].locked = value;
48072         if(!suppressEvent){
48073             this.fireEvent("columnlockchange", this, colIndex, value);
48074         }
48075     },
48076
48077     getTotalLockedWidth : function(){
48078         var totalWidth = 0;
48079         for(var i = 0; i < this.config.length; i++){
48080             if(this.isLocked(i) && !this.isHidden(i)){
48081                 this.totalWidth += this.getColumnWidth(i);
48082             }
48083         }
48084         return totalWidth;
48085     },
48086
48087     getLockedCount : function(){
48088         for(var i = 0, len = this.config.length; i < len; i++){
48089             if(!this.isLocked(i)){
48090                 return i;
48091             }
48092         }
48093     },
48094
48095     /**
48096      * Returns the number of columns.
48097      * @return {Number}
48098      */
48099     getColumnCount : function(visibleOnly){
48100         if(visibleOnly === true){
48101             var c = 0;
48102             for(var i = 0, len = this.config.length; i < len; i++){
48103                 if(!this.isHidden(i)){
48104                     c++;
48105                 }
48106             }
48107             return c;
48108         }
48109         return this.config.length;
48110     },
48111
48112     /**
48113      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
48114      * @param {Function} fn
48115      * @param {Object} scope (optional)
48116      * @return {Array} result
48117      */
48118     getColumnsBy : function(fn, scope){
48119         var r = [];
48120         for(var i = 0, len = this.config.length; i < len; i++){
48121             var c = this.config[i];
48122             if(fn.call(scope||this, c, i) === true){
48123                 r[r.length] = c;
48124             }
48125         }
48126         return r;
48127     },
48128
48129     /**
48130      * Returns true if the specified column is sortable.
48131      * @param {Number} col The column index
48132      * @return {Boolean}
48133      */
48134     isSortable : function(col){
48135         if(typeof this.config[col].sortable == "undefined"){
48136             return this.defaultSortable;
48137         }
48138         return this.config[col].sortable;
48139     },
48140
48141     /**
48142      * Returns the rendering (formatting) function defined for the column.
48143      * @param {Number} col The column index.
48144      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
48145      */
48146     getRenderer : function(col){
48147         if(!this.config[col].renderer){
48148             return Roo.grid.ColumnModel.defaultRenderer;
48149         }
48150         return this.config[col].renderer;
48151     },
48152
48153     /**
48154      * Sets the rendering (formatting) function for a column.
48155      * @param {Number} col The column index
48156      * @param {Function} fn The function to use to process the cell's raw data
48157      * to return HTML markup for the grid view. The render function is called with
48158      * the following parameters:<ul>
48159      * <li>Data value.</li>
48160      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
48161      * <li>css A CSS style string to apply to the table cell.</li>
48162      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
48163      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
48164      * <li>Row index</li>
48165      * <li>Column index</li>
48166      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
48167      */
48168     setRenderer : function(col, fn){
48169         this.config[col].renderer = fn;
48170     },
48171
48172     /**
48173      * Returns the width for the specified column.
48174      * @param {Number} col The column index
48175      * @return {Number}
48176      */
48177     getColumnWidth : function(col){
48178         return this.config[col].width || this.defaultWidth;
48179     },
48180
48181     /**
48182      * Sets the width for a column.
48183      * @param {Number} col The column index
48184      * @param {Number} width The new width
48185      */
48186     setColumnWidth : function(col, width, suppressEvent){
48187         this.config[col].width = width;
48188         this.totalWidth = null;
48189         if(!suppressEvent){
48190              this.fireEvent("widthchange", this, col, width);
48191         }
48192     },
48193
48194     /**
48195      * Returns the total width of all columns.
48196      * @param {Boolean} includeHidden True to include hidden column widths
48197      * @return {Number}
48198      */
48199     getTotalWidth : function(includeHidden){
48200         if(!this.totalWidth){
48201             this.totalWidth = 0;
48202             for(var i = 0, len = this.config.length; i < len; i++){
48203                 if(includeHidden || !this.isHidden(i)){
48204                     this.totalWidth += this.getColumnWidth(i);
48205                 }
48206             }
48207         }
48208         return this.totalWidth;
48209     },
48210
48211     /**
48212      * Returns the header for the specified column.
48213      * @param {Number} col The column index
48214      * @return {String}
48215      */
48216     getColumnHeader : function(col){
48217         return this.config[col].header;
48218     },
48219
48220     /**
48221      * Sets the header for a column.
48222      * @param {Number} col The column index
48223      * @param {String} header The new header
48224      */
48225     setColumnHeader : function(col, header){
48226         this.config[col].header = header;
48227         this.fireEvent("headerchange", this, col, header);
48228     },
48229
48230     /**
48231      * Returns the tooltip for the specified column.
48232      * @param {Number} col The column index
48233      * @return {String}
48234      */
48235     getColumnTooltip : function(col){
48236             return this.config[col].tooltip;
48237     },
48238     /**
48239      * Sets the tooltip for a column.
48240      * @param {Number} col The column index
48241      * @param {String} tooltip The new tooltip
48242      */
48243     setColumnTooltip : function(col, tooltip){
48244             this.config[col].tooltip = tooltip;
48245     },
48246
48247     /**
48248      * Returns the dataIndex for the specified column.
48249      * @param {Number} col The column index
48250      * @return {Number}
48251      */
48252     getDataIndex : function(col){
48253         return this.config[col].dataIndex;
48254     },
48255
48256     /**
48257      * Sets the dataIndex for a column.
48258      * @param {Number} col The column index
48259      * @param {Number} dataIndex The new dataIndex
48260      */
48261     setDataIndex : function(col, dataIndex){
48262         this.config[col].dataIndex = dataIndex;
48263     },
48264
48265     
48266     
48267     /**
48268      * Returns true if the cell is editable.
48269      * @param {Number} colIndex The column index
48270      * @param {Number} rowIndex The row index
48271      * @return {Boolean}
48272      */
48273     isCellEditable : function(colIndex, rowIndex){
48274         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
48275     },
48276
48277     /**
48278      * Returns the editor defined for the cell/column.
48279      * return false or null to disable editing.
48280      * @param {Number} colIndex The column index
48281      * @param {Number} rowIndex The row index
48282      * @return {Object}
48283      */
48284     getCellEditor : function(colIndex, rowIndex){
48285         return this.config[colIndex].editor;
48286     },
48287
48288     /**
48289      * Sets if a column is editable.
48290      * @param {Number} col The column index
48291      * @param {Boolean} editable True if the column is editable
48292      */
48293     setEditable : function(col, editable){
48294         this.config[col].editable = editable;
48295     },
48296
48297
48298     /**
48299      * Returns true if the column is hidden.
48300      * @param {Number} colIndex The column index
48301      * @return {Boolean}
48302      */
48303     isHidden : function(colIndex){
48304         return this.config[colIndex].hidden;
48305     },
48306
48307
48308     /**
48309      * Returns true if the column width cannot be changed
48310      */
48311     isFixed : function(colIndex){
48312         return this.config[colIndex].fixed;
48313     },
48314
48315     /**
48316      * Returns true if the column can be resized
48317      * @return {Boolean}
48318      */
48319     isResizable : function(colIndex){
48320         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
48321     },
48322     /**
48323      * Sets if a column is hidden.
48324      * @param {Number} colIndex The column index
48325      * @param {Boolean} hidden True if the column is hidden
48326      */
48327     setHidden : function(colIndex, hidden){
48328         this.config[colIndex].hidden = hidden;
48329         this.totalWidth = null;
48330         this.fireEvent("hiddenchange", this, colIndex, hidden);
48331     },
48332
48333     /**
48334      * Sets the editor for a column.
48335      * @param {Number} col The column index
48336      * @param {Object} editor The editor object
48337      */
48338     setEditor : function(col, editor){
48339         this.config[col].editor = editor;
48340     }
48341 });
48342
48343 Roo.grid.ColumnModel.defaultRenderer = function(value){
48344         if(typeof value == "string" && value.length < 1){
48345             return "&#160;";
48346         }
48347         return value;
48348 };
48349
48350 // Alias for backwards compatibility
48351 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
48352 /*
48353  * Based on:
48354  * Ext JS Library 1.1.1
48355  * Copyright(c) 2006-2007, Ext JS, LLC.
48356  *
48357  * Originally Released Under LGPL - original licence link has changed is not relivant.
48358  *
48359  * Fork - LGPL
48360  * <script type="text/javascript">
48361  */
48362
48363 /**
48364  * @class Roo.grid.AbstractSelectionModel
48365  * @extends Roo.util.Observable
48366  * Abstract base class for grid SelectionModels.  It provides the interface that should be
48367  * implemented by descendant classes.  This class should not be directly instantiated.
48368  * @constructor
48369  */
48370 Roo.grid.AbstractSelectionModel = function(){
48371     this.locked = false;
48372     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
48373 };
48374
48375 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
48376     /** @ignore Called by the grid automatically. Do not call directly. */
48377     init : function(grid){
48378         this.grid = grid;
48379         this.initEvents();
48380     },
48381
48382     /**
48383      * Locks the selections.
48384      */
48385     lock : function(){
48386         this.locked = true;
48387     },
48388
48389     /**
48390      * Unlocks the selections.
48391      */
48392     unlock : function(){
48393         this.locked = false;
48394     },
48395
48396     /**
48397      * Returns true if the selections are locked.
48398      * @return {Boolean}
48399      */
48400     isLocked : function(){
48401         return this.locked;
48402     }
48403 });/*
48404  * Based on:
48405  * Ext JS Library 1.1.1
48406  * Copyright(c) 2006-2007, Ext JS, LLC.
48407  *
48408  * Originally Released Under LGPL - original licence link has changed is not relivant.
48409  *
48410  * Fork - LGPL
48411  * <script type="text/javascript">
48412  */
48413 /**
48414  * @extends Roo.grid.AbstractSelectionModel
48415  * @class Roo.grid.RowSelectionModel
48416  * The default SelectionModel used by {@link Roo.grid.Grid}.
48417  * It supports multiple selections and keyboard selection/navigation. 
48418  * @constructor
48419  * @param {Object} config
48420  */
48421 Roo.grid.RowSelectionModel = function(config){
48422     Roo.apply(this, config);
48423     this.selections = new Roo.util.MixedCollection(false, function(o){
48424         return o.id;
48425     });
48426
48427     this.last = false;
48428     this.lastActive = false;
48429
48430     this.addEvents({
48431         /**
48432              * @event selectionchange
48433              * Fires when the selection changes
48434              * @param {SelectionModel} this
48435              */
48436             "selectionchange" : true,
48437         /**
48438              * @event afterselectionchange
48439              * Fires after the selection changes (eg. by key press or clicking)
48440              * @param {SelectionModel} this
48441              */
48442             "afterselectionchange" : true,
48443         /**
48444              * @event beforerowselect
48445              * Fires when a row is selected being selected, return false to cancel.
48446              * @param {SelectionModel} this
48447              * @param {Number} rowIndex The selected index
48448              * @param {Boolean} keepExisting False if other selections will be cleared
48449              */
48450             "beforerowselect" : true,
48451         /**
48452              * @event rowselect
48453              * Fires when a row is selected.
48454              * @param {SelectionModel} this
48455              * @param {Number} rowIndex The selected index
48456              * @param {Roo.data.Record} r The record
48457              */
48458             "rowselect" : true,
48459         /**
48460              * @event rowdeselect
48461              * Fires when a row is deselected.
48462              * @param {SelectionModel} this
48463              * @param {Number} rowIndex The selected index
48464              */
48465         "rowdeselect" : true
48466     });
48467     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
48468     this.locked = false;
48469 };
48470
48471 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
48472     /**
48473      * @cfg {Boolean} singleSelect
48474      * True to allow selection of only one row at a time (defaults to false)
48475      */
48476     singleSelect : false,
48477
48478     // private
48479     initEvents : function(){
48480
48481         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
48482             this.grid.on("mousedown", this.handleMouseDown, this);
48483         }else{ // allow click to work like normal
48484             this.grid.on("rowclick", this.handleDragableRowClick, this);
48485         }
48486
48487         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
48488             "up" : function(e){
48489                 if(!e.shiftKey){
48490                     this.selectPrevious(e.shiftKey);
48491                 }else if(this.last !== false && this.lastActive !== false){
48492                     var last = this.last;
48493                     this.selectRange(this.last,  this.lastActive-1);
48494                     this.grid.getView().focusRow(this.lastActive);
48495                     if(last !== false){
48496                         this.last = last;
48497                     }
48498                 }else{
48499                     this.selectFirstRow();
48500                 }
48501                 this.fireEvent("afterselectionchange", this);
48502             },
48503             "down" : function(e){
48504                 if(!e.shiftKey){
48505                     this.selectNext(e.shiftKey);
48506                 }else if(this.last !== false && this.lastActive !== false){
48507                     var last = this.last;
48508                     this.selectRange(this.last,  this.lastActive+1);
48509                     this.grid.getView().focusRow(this.lastActive);
48510                     if(last !== false){
48511                         this.last = last;
48512                     }
48513                 }else{
48514                     this.selectFirstRow();
48515                 }
48516                 this.fireEvent("afterselectionchange", this);
48517             },
48518             scope: this
48519         });
48520
48521         var view = this.grid.view;
48522         view.on("refresh", this.onRefresh, this);
48523         view.on("rowupdated", this.onRowUpdated, this);
48524         view.on("rowremoved", this.onRemove, this);
48525     },
48526
48527     // private
48528     onRefresh : function(){
48529         var ds = this.grid.dataSource, i, v = this.grid.view;
48530         var s = this.selections;
48531         s.each(function(r){
48532             if((i = ds.indexOfId(r.id)) != -1){
48533                 v.onRowSelect(i);
48534             }else{
48535                 s.remove(r);
48536             }
48537         });
48538     },
48539
48540     // private
48541     onRemove : function(v, index, r){
48542         this.selections.remove(r);
48543     },
48544
48545     // private
48546     onRowUpdated : function(v, index, r){
48547         if(this.isSelected(r)){
48548             v.onRowSelect(index);
48549         }
48550     },
48551
48552     /**
48553      * Select records.
48554      * @param {Array} records The records to select
48555      * @param {Boolean} keepExisting (optional) True to keep existing selections
48556      */
48557     selectRecords : function(records, keepExisting){
48558         if(!keepExisting){
48559             this.clearSelections();
48560         }
48561         var ds = this.grid.dataSource;
48562         for(var i = 0, len = records.length; i < len; i++){
48563             this.selectRow(ds.indexOf(records[i]), true);
48564         }
48565     },
48566
48567     /**
48568      * Gets the number of selected rows.
48569      * @return {Number}
48570      */
48571     getCount : function(){
48572         return this.selections.length;
48573     },
48574
48575     /**
48576      * Selects the first row in the grid.
48577      */
48578     selectFirstRow : function(){
48579         this.selectRow(0);
48580     },
48581
48582     /**
48583      * Select the last row.
48584      * @param {Boolean} keepExisting (optional) True to keep existing selections
48585      */
48586     selectLastRow : function(keepExisting){
48587         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
48588     },
48589
48590     /**
48591      * Selects the row immediately following the last selected row.
48592      * @param {Boolean} keepExisting (optional) True to keep existing selections
48593      */
48594     selectNext : function(keepExisting){
48595         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
48596             this.selectRow(this.last+1, keepExisting);
48597             this.grid.getView().focusRow(this.last);
48598         }
48599     },
48600
48601     /**
48602      * Selects the row that precedes the last selected row.
48603      * @param {Boolean} keepExisting (optional) True to keep existing selections
48604      */
48605     selectPrevious : function(keepExisting){
48606         if(this.last){
48607             this.selectRow(this.last-1, keepExisting);
48608             this.grid.getView().focusRow(this.last);
48609         }
48610     },
48611
48612     /**
48613      * Returns the selected records
48614      * @return {Array} Array of selected records
48615      */
48616     getSelections : function(){
48617         return [].concat(this.selections.items);
48618     },
48619
48620     /**
48621      * Returns the first selected record.
48622      * @return {Record}
48623      */
48624     getSelected : function(){
48625         return this.selections.itemAt(0);
48626     },
48627
48628
48629     /**
48630      * Clears all selections.
48631      */
48632     clearSelections : function(fast){
48633         if(this.locked) return;
48634         if(fast !== true){
48635             var ds = this.grid.dataSource;
48636             var s = this.selections;
48637             s.each(function(r){
48638                 this.deselectRow(ds.indexOfId(r.id));
48639             }, this);
48640             s.clear();
48641         }else{
48642             this.selections.clear();
48643         }
48644         this.last = false;
48645     },
48646
48647
48648     /**
48649      * Selects all rows.
48650      */
48651     selectAll : function(){
48652         if(this.locked) return;
48653         this.selections.clear();
48654         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
48655             this.selectRow(i, true);
48656         }
48657     },
48658
48659     /**
48660      * Returns True if there is a selection.
48661      * @return {Boolean}
48662      */
48663     hasSelection : function(){
48664         return this.selections.length > 0;
48665     },
48666
48667     /**
48668      * Returns True if the specified row is selected.
48669      * @param {Number/Record} record The record or index of the record to check
48670      * @return {Boolean}
48671      */
48672     isSelected : function(index){
48673         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
48674         return (r && this.selections.key(r.id) ? true : false);
48675     },
48676
48677     /**
48678      * Returns True if the specified record id is selected.
48679      * @param {String} id The id of record to check
48680      * @return {Boolean}
48681      */
48682     isIdSelected : function(id){
48683         return (this.selections.key(id) ? true : false);
48684     },
48685
48686     // private
48687     handleMouseDown : function(e, t){
48688         var view = this.grid.getView(), rowIndex;
48689         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
48690             return;
48691         };
48692         if(e.shiftKey && this.last !== false){
48693             var last = this.last;
48694             this.selectRange(last, rowIndex, e.ctrlKey);
48695             this.last = last; // reset the last
48696             view.focusRow(rowIndex);
48697         }else{
48698             var isSelected = this.isSelected(rowIndex);
48699             if(e.button !== 0 && isSelected){
48700                 view.focusRow(rowIndex);
48701             }else if(e.ctrlKey && isSelected){
48702                 this.deselectRow(rowIndex);
48703             }else if(!isSelected){
48704                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
48705                 view.focusRow(rowIndex);
48706             }
48707         }
48708         this.fireEvent("afterselectionchange", this);
48709     },
48710     // private
48711     handleDragableRowClick :  function(grid, rowIndex, e) 
48712     {
48713         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
48714             this.selectRow(rowIndex, false);
48715             grid.view.focusRow(rowIndex);
48716              this.fireEvent("afterselectionchange", this);
48717         }
48718     },
48719     
48720     /**
48721      * Selects multiple rows.
48722      * @param {Array} rows Array of the indexes of the row to select
48723      * @param {Boolean} keepExisting (optional) True to keep existing selections
48724      */
48725     selectRows : function(rows, keepExisting){
48726         if(!keepExisting){
48727             this.clearSelections();
48728         }
48729         for(var i = 0, len = rows.length; i < len; i++){
48730             this.selectRow(rows[i], true);
48731         }
48732     },
48733
48734     /**
48735      * Selects a range of rows. All rows in between startRow and endRow are also selected.
48736      * @param {Number} startRow The index of the first row in the range
48737      * @param {Number} endRow The index of the last row in the range
48738      * @param {Boolean} keepExisting (optional) True to retain existing selections
48739      */
48740     selectRange : function(startRow, endRow, keepExisting){
48741         if(this.locked) return;
48742         if(!keepExisting){
48743             this.clearSelections();
48744         }
48745         if(startRow <= endRow){
48746             for(var i = startRow; i <= endRow; i++){
48747                 this.selectRow(i, true);
48748             }
48749         }else{
48750             for(var i = startRow; i >= endRow; i--){
48751                 this.selectRow(i, true);
48752             }
48753         }
48754     },
48755
48756     /**
48757      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
48758      * @param {Number} startRow The index of the first row in the range
48759      * @param {Number} endRow The index of the last row in the range
48760      */
48761     deselectRange : function(startRow, endRow, preventViewNotify){
48762         if(this.locked) return;
48763         for(var i = startRow; i <= endRow; i++){
48764             this.deselectRow(i, preventViewNotify);
48765         }
48766     },
48767
48768     /**
48769      * Selects a row.
48770      * @param {Number} row The index of the row to select
48771      * @param {Boolean} keepExisting (optional) True to keep existing selections
48772      */
48773     selectRow : function(index, keepExisting, preventViewNotify){
48774         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
48775         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
48776             if(!keepExisting || this.singleSelect){
48777                 this.clearSelections();
48778             }
48779             var r = this.grid.dataSource.getAt(index);
48780             this.selections.add(r);
48781             this.last = this.lastActive = index;
48782             if(!preventViewNotify){
48783                 this.grid.getView().onRowSelect(index);
48784             }
48785             this.fireEvent("rowselect", this, index, r);
48786             this.fireEvent("selectionchange", this);
48787         }
48788     },
48789
48790     /**
48791      * Deselects a row.
48792      * @param {Number} row The index of the row to deselect
48793      */
48794     deselectRow : function(index, preventViewNotify){
48795         if(this.locked) return;
48796         if(this.last == index){
48797             this.last = false;
48798         }
48799         if(this.lastActive == index){
48800             this.lastActive = false;
48801         }
48802         var r = this.grid.dataSource.getAt(index);
48803         this.selections.remove(r);
48804         if(!preventViewNotify){
48805             this.grid.getView().onRowDeselect(index);
48806         }
48807         this.fireEvent("rowdeselect", this, index);
48808         this.fireEvent("selectionchange", this);
48809     },
48810
48811     // private
48812     restoreLast : function(){
48813         if(this._last){
48814             this.last = this._last;
48815         }
48816     },
48817
48818     // private
48819     acceptsNav : function(row, col, cm){
48820         return !cm.isHidden(col) && cm.isCellEditable(col, row);
48821     },
48822
48823     // private
48824     onEditorKey : function(field, e){
48825         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
48826         if(k == e.TAB){
48827             e.stopEvent();
48828             ed.completeEdit();
48829             if(e.shiftKey){
48830                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
48831             }else{
48832                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
48833             }
48834         }else if(k == e.ENTER && !e.ctrlKey){
48835             e.stopEvent();
48836             ed.completeEdit();
48837             if(e.shiftKey){
48838                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
48839             }else{
48840                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
48841             }
48842         }else if(k == e.ESC){
48843             ed.cancelEdit();
48844         }
48845         if(newCell){
48846             g.startEditing(newCell[0], newCell[1]);
48847         }
48848     }
48849 });/*
48850  * Based on:
48851  * Ext JS Library 1.1.1
48852  * Copyright(c) 2006-2007, Ext JS, LLC.
48853  *
48854  * Originally Released Under LGPL - original licence link has changed is not relivant.
48855  *
48856  * Fork - LGPL
48857  * <script type="text/javascript">
48858  */
48859 /**
48860  * @class Roo.grid.CellSelectionModel
48861  * @extends Roo.grid.AbstractSelectionModel
48862  * This class provides the basic implementation for cell selection in a grid.
48863  * @constructor
48864  * @param {Object} config The object containing the configuration of this model.
48865  */
48866 Roo.grid.CellSelectionModel = function(config){
48867     Roo.apply(this, config);
48868
48869     this.selection = null;
48870
48871     this.addEvents({
48872         /**
48873              * @event beforerowselect
48874              * Fires before a cell is selected.
48875              * @param {SelectionModel} this
48876              * @param {Number} rowIndex The selected row index
48877              * @param {Number} colIndex The selected cell index
48878              */
48879             "beforecellselect" : true,
48880         /**
48881              * @event cellselect
48882              * Fires when a cell is selected.
48883              * @param {SelectionModel} this
48884              * @param {Number} rowIndex The selected row index
48885              * @param {Number} colIndex The selected cell index
48886              */
48887             "cellselect" : true,
48888         /**
48889              * @event selectionchange
48890              * Fires when the active selection changes.
48891              * @param {SelectionModel} this
48892              * @param {Object} selection null for no selection or an object (o) with two properties
48893                 <ul>
48894                 <li>o.record: the record object for the row the selection is in</li>
48895                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
48896                 </ul>
48897              */
48898             "selectionchange" : true
48899     });
48900     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
48901 };
48902
48903 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
48904
48905     /** @ignore */
48906     initEvents : function(){
48907         this.grid.on("mousedown", this.handleMouseDown, this);
48908         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
48909         var view = this.grid.view;
48910         view.on("refresh", this.onViewChange, this);
48911         view.on("rowupdated", this.onRowUpdated, this);
48912         view.on("beforerowremoved", this.clearSelections, this);
48913         view.on("beforerowsinserted", this.clearSelections, this);
48914         if(this.grid.isEditor){
48915             this.grid.on("beforeedit", this.beforeEdit,  this);
48916         }
48917     },
48918
48919         //private
48920     beforeEdit : function(e){
48921         this.select(e.row, e.column, false, true, e.record);
48922     },
48923
48924         //private
48925     onRowUpdated : function(v, index, r){
48926         if(this.selection && this.selection.record == r){
48927             v.onCellSelect(index, this.selection.cell[1]);
48928         }
48929     },
48930
48931         //private
48932     onViewChange : function(){
48933         this.clearSelections(true);
48934     },
48935
48936         /**
48937          * Returns the currently selected cell,.
48938          * @return {Array} The selected cell (row, column) or null if none selected.
48939          */
48940     getSelectedCell : function(){
48941         return this.selection ? this.selection.cell : null;
48942     },
48943
48944     /**
48945      * Clears all selections.
48946      * @param {Boolean} true to prevent the gridview from being notified about the change.
48947      */
48948     clearSelections : function(preventNotify){
48949         var s = this.selection;
48950         if(s){
48951             if(preventNotify !== true){
48952                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
48953             }
48954             this.selection = null;
48955             this.fireEvent("selectionchange", this, null);
48956         }
48957     },
48958
48959     /**
48960      * Returns true if there is a selection.
48961      * @return {Boolean}
48962      */
48963     hasSelection : function(){
48964         return this.selection ? true : false;
48965     },
48966
48967     /** @ignore */
48968     handleMouseDown : function(e, t){
48969         var v = this.grid.getView();
48970         if(this.isLocked()){
48971             return;
48972         };
48973         var row = v.findRowIndex(t);
48974         var cell = v.findCellIndex(t);
48975         if(row !== false && cell !== false){
48976             this.select(row, cell);
48977         }
48978     },
48979
48980     /**
48981      * Selects a cell.
48982      * @param {Number} rowIndex
48983      * @param {Number} collIndex
48984      */
48985     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
48986         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
48987             this.clearSelections();
48988             r = r || this.grid.dataSource.getAt(rowIndex);
48989             this.selection = {
48990                 record : r,
48991                 cell : [rowIndex, colIndex]
48992             };
48993             if(!preventViewNotify){
48994                 var v = this.grid.getView();
48995                 v.onCellSelect(rowIndex, colIndex);
48996                 if(preventFocus !== true){
48997                     v.focusCell(rowIndex, colIndex);
48998                 }
48999             }
49000             this.fireEvent("cellselect", this, rowIndex, colIndex);
49001             this.fireEvent("selectionchange", this, this.selection);
49002         }
49003     },
49004
49005         //private
49006     isSelectable : function(rowIndex, colIndex, cm){
49007         return !cm.isHidden(colIndex);
49008     },
49009
49010     /** @ignore */
49011     handleKeyDown : function(e){
49012         if(!e.isNavKeyPress()){
49013             return;
49014         }
49015         var g = this.grid, s = this.selection;
49016         if(!s){
49017             e.stopEvent();
49018             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
49019             if(cell){
49020                 this.select(cell[0], cell[1]);
49021             }
49022             return;
49023         }
49024         var sm = this;
49025         var walk = function(row, col, step){
49026             return g.walkCells(row, col, step, sm.isSelectable,  sm);
49027         };
49028         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
49029         var newCell;
49030
49031         switch(k){
49032              case e.TAB:
49033                  if(e.shiftKey){
49034                      newCell = walk(r, c-1, -1);
49035                  }else{
49036                      newCell = walk(r, c+1, 1);
49037                  }
49038              break;
49039              case e.DOWN:
49040                  newCell = walk(r+1, c, 1);
49041              break;
49042              case e.UP:
49043                  newCell = walk(r-1, c, -1);
49044              break;
49045              case e.RIGHT:
49046                  newCell = walk(r, c+1, 1);
49047              break;
49048              case e.LEFT:
49049                  newCell = walk(r, c-1, -1);
49050              break;
49051              case e.ENTER:
49052                  if(g.isEditor && !g.editing){
49053                     g.startEditing(r, c);
49054                     e.stopEvent();
49055                     return;
49056                 }
49057              break;
49058         };
49059         if(newCell){
49060             this.select(newCell[0], newCell[1]);
49061             e.stopEvent();
49062         }
49063     },
49064
49065     acceptsNav : function(row, col, cm){
49066         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49067     },
49068
49069     onEditorKey : function(field, e){
49070         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49071         if(k == e.TAB){
49072             if(e.shiftKey){
49073                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49074             }else{
49075                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49076             }
49077             e.stopEvent();
49078         }else if(k == e.ENTER && !e.ctrlKey){
49079             ed.completeEdit();
49080             e.stopEvent();
49081         }else if(k == e.ESC){
49082             ed.cancelEdit();
49083         }
49084         if(newCell){
49085             g.startEditing(newCell[0], newCell[1]);
49086         }
49087     }
49088 });/*
49089  * Based on:
49090  * Ext JS Library 1.1.1
49091  * Copyright(c) 2006-2007, Ext JS, LLC.
49092  *
49093  * Originally Released Under LGPL - original licence link has changed is not relivant.
49094  *
49095  * Fork - LGPL
49096  * <script type="text/javascript">
49097  */
49098  
49099 /**
49100  * @class Roo.grid.EditorGrid
49101  * @extends Roo.grid.Grid
49102  * Class for creating and editable grid.
49103  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
49104  * The container MUST have some type of size defined for the grid to fill. The container will be 
49105  * automatically set to position relative if it isn't already.
49106  * @param {Object} dataSource The data model to bind to
49107  * @param {Object} colModel The column model with info about this grid's columns
49108  */
49109 Roo.grid.EditorGrid = function(container, config){
49110     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
49111     this.getGridEl().addClass("xedit-grid");
49112
49113     if(!this.selModel){
49114         this.selModel = new Roo.grid.CellSelectionModel();
49115     }
49116
49117     this.activeEditor = null;
49118
49119         this.addEvents({
49120             /**
49121              * @event beforeedit
49122              * Fires before cell editing is triggered. The edit event object has the following properties <br />
49123              * <ul style="padding:5px;padding-left:16px;">
49124              * <li>grid - This grid</li>
49125              * <li>record - The record being edited</li>
49126              * <li>field - The field name being edited</li>
49127              * <li>value - The value for the field being edited.</li>
49128              * <li>row - The grid row index</li>
49129              * <li>column - The grid column index</li>
49130              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49131              * </ul>
49132              * @param {Object} e An edit event (see above for description)
49133              */
49134             "beforeedit" : true,
49135             /**
49136              * @event afteredit
49137              * Fires after a cell is edited. <br />
49138              * <ul style="padding:5px;padding-left:16px;">
49139              * <li>grid - This grid</li>
49140              * <li>record - The record being edited</li>
49141              * <li>field - The field name being edited</li>
49142              * <li>value - The value being set</li>
49143              * <li>originalValue - The original value for the field, before the edit.</li>
49144              * <li>row - The grid row index</li>
49145              * <li>column - The grid column index</li>
49146              * </ul>
49147              * @param {Object} e An edit event (see above for description)
49148              */
49149             "afteredit" : true,
49150             /**
49151              * @event validateedit
49152              * Fires after a cell is edited, but before the value is set in the record. 
49153          * You can use this to modify the value being set in the field, Return false
49154              * to cancel the change. The edit event object has the following properties <br />
49155              * <ul style="padding:5px;padding-left:16px;">
49156          * <li>editor - This editor</li>
49157              * <li>grid - This grid</li>
49158              * <li>record - The record being edited</li>
49159              * <li>field - The field name being edited</li>
49160              * <li>value - The value being set</li>
49161              * <li>originalValue - The original value for the field, before the edit.</li>
49162              * <li>row - The grid row index</li>
49163              * <li>column - The grid column index</li>
49164              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49165              * </ul>
49166              * @param {Object} e An edit event (see above for description)
49167              */
49168             "validateedit" : true
49169         });
49170     this.on("bodyscroll", this.stopEditing,  this);
49171     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
49172 };
49173
49174 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
49175     /**
49176      * @cfg {Number} clicksToEdit
49177      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
49178      */
49179     clicksToEdit: 2,
49180
49181     // private
49182     isEditor : true,
49183     // private
49184     trackMouseOver: false, // causes very odd FF errors
49185
49186     onCellDblClick : function(g, row, col){
49187         this.startEditing(row, col);
49188     },
49189
49190     onEditComplete : function(ed, value, startValue){
49191         this.editing = false;
49192         this.activeEditor = null;
49193         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
49194         var r = ed.record;
49195         var field = this.colModel.getDataIndex(ed.col);
49196         var e = {
49197             grid: this,
49198             record: r,
49199             field: field,
49200             originalValue: startValue,
49201             value: value,
49202             row: ed.row,
49203             column: ed.col,
49204             cancel:false,
49205             editor: ed
49206         };
49207         if(String(value) !== String(startValue)){
49208             
49209             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
49210                 r.set(field, e.value);
49211                 delete e.cancel; //?? why!!!
49212                 this.fireEvent("afteredit", e);
49213             }
49214         } else {
49215             this.fireEvent("afteredit", e); // always fir it!
49216         }
49217         this.view.focusCell(ed.row, ed.col);
49218     },
49219
49220     /**
49221      * Starts editing the specified for the specified row/column
49222      * @param {Number} rowIndex
49223      * @param {Number} colIndex
49224      */
49225     startEditing : function(row, col){
49226         this.stopEditing();
49227         if(this.colModel.isCellEditable(col, row)){
49228             this.view.ensureVisible(row, col, true);
49229             var r = this.dataSource.getAt(row);
49230             var field = this.colModel.getDataIndex(col);
49231             var e = {
49232                 grid: this,
49233                 record: r,
49234                 field: field,
49235                 value: r.data[field],
49236                 row: row,
49237                 column: col,
49238                 cancel:false
49239             };
49240             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
49241                 this.editing = true;
49242                 var ed = this.colModel.getCellEditor(col, row);
49243                 
49244                 if (!ed) {
49245                     return;
49246                 }
49247                 if(!ed.rendered){
49248                     ed.render(ed.parentEl || document.body);
49249                 }
49250                 ed.field.reset();
49251                 (function(){ // complex but required for focus issues in safari, ie and opera
49252                     ed.row = row;
49253                     ed.col = col;
49254                     ed.record = r;
49255                     ed.on("complete", this.onEditComplete, this, {single: true});
49256                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
49257                     this.activeEditor = ed;
49258                     var v = r.data[field];
49259                     ed.startEdit(this.view.getCell(row, col), v);
49260                 }).defer(50, this);
49261             }
49262         }
49263     },
49264         
49265     /**
49266      * Stops any active editing
49267      */
49268     stopEditing : function(){
49269         if(this.activeEditor){
49270             this.activeEditor.completeEdit();
49271         }
49272         this.activeEditor = null;
49273     }
49274 });/*
49275  * Based on:
49276  * Ext JS Library 1.1.1
49277  * Copyright(c) 2006-2007, Ext JS, LLC.
49278  *
49279  * Originally Released Under LGPL - original licence link has changed is not relivant.
49280  *
49281  * Fork - LGPL
49282  * <script type="text/javascript">
49283  */
49284
49285 // private - not really -- you end up using it !
49286 // This is a support class used internally by the Grid components
49287
49288 /**
49289  * @class Roo.grid.GridEditor
49290  * @extends Roo.Editor
49291  * Class for creating and editable grid elements.
49292  * @param {Object} config any settings (must include field)
49293  */
49294 Roo.grid.GridEditor = function(field, config){
49295     if (!config && field.field) {
49296         config = field;
49297         field = Roo.factory(config.field, Roo.form);
49298     }
49299     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
49300     field.monitorTab = false;
49301 };
49302
49303 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
49304     
49305     /**
49306      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
49307      */
49308     
49309     alignment: "tl-tl",
49310     autoSize: "width",
49311     hideEl : false,
49312     cls: "x-small-editor x-grid-editor",
49313     shim:false,
49314     shadow:"frame"
49315 });/*
49316  * Based on:
49317  * Ext JS Library 1.1.1
49318  * Copyright(c) 2006-2007, Ext JS, LLC.
49319  *
49320  * Originally Released Under LGPL - original licence link has changed is not relivant.
49321  *
49322  * Fork - LGPL
49323  * <script type="text/javascript">
49324  */
49325   
49326
49327   
49328 Roo.grid.PropertyRecord = Roo.data.Record.create([
49329     {name:'name',type:'string'},  'value'
49330 ]);
49331
49332
49333 Roo.grid.PropertyStore = function(grid, source){
49334     this.grid = grid;
49335     this.store = new Roo.data.Store({
49336         recordType : Roo.grid.PropertyRecord
49337     });
49338     this.store.on('update', this.onUpdate,  this);
49339     if(source){
49340         this.setSource(source);
49341     }
49342     Roo.grid.PropertyStore.superclass.constructor.call(this);
49343 };
49344
49345
49346
49347 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
49348     setSource : function(o){
49349         this.source = o;
49350         this.store.removeAll();
49351         var data = [];
49352         for(var k in o){
49353             if(this.isEditableValue(o[k])){
49354                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
49355             }
49356         }
49357         this.store.loadRecords({records: data}, {}, true);
49358     },
49359
49360     onUpdate : function(ds, record, type){
49361         if(type == Roo.data.Record.EDIT){
49362             var v = record.data['value'];
49363             var oldValue = record.modified['value'];
49364             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
49365                 this.source[record.id] = v;
49366                 record.commit();
49367                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
49368             }else{
49369                 record.reject();
49370             }
49371         }
49372     },
49373
49374     getProperty : function(row){
49375        return this.store.getAt(row);
49376     },
49377
49378     isEditableValue: function(val){
49379         if(val && val instanceof Date){
49380             return true;
49381         }else if(typeof val == 'object' || typeof val == 'function'){
49382             return false;
49383         }
49384         return true;
49385     },
49386
49387     setValue : function(prop, value){
49388         this.source[prop] = value;
49389         this.store.getById(prop).set('value', value);
49390     },
49391
49392     getSource : function(){
49393         return this.source;
49394     }
49395 });
49396
49397 Roo.grid.PropertyColumnModel = function(grid, store){
49398     this.grid = grid;
49399     var g = Roo.grid;
49400     g.PropertyColumnModel.superclass.constructor.call(this, [
49401         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
49402         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
49403     ]);
49404     this.store = store;
49405     this.bselect = Roo.DomHelper.append(document.body, {
49406         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
49407             {tag: 'option', value: 'true', html: 'true'},
49408             {tag: 'option', value: 'false', html: 'false'}
49409         ]
49410     });
49411     Roo.id(this.bselect);
49412     var f = Roo.form;
49413     this.editors = {
49414         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
49415         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
49416         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
49417         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
49418         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
49419     };
49420     this.renderCellDelegate = this.renderCell.createDelegate(this);
49421     this.renderPropDelegate = this.renderProp.createDelegate(this);
49422 };
49423
49424 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
49425     
49426     
49427     nameText : 'Name',
49428     valueText : 'Value',
49429     
49430     dateFormat : 'm/j/Y',
49431     
49432     
49433     renderDate : function(dateVal){
49434         return dateVal.dateFormat(this.dateFormat);
49435     },
49436
49437     renderBool : function(bVal){
49438         return bVal ? 'true' : 'false';
49439     },
49440
49441     isCellEditable : function(colIndex, rowIndex){
49442         return colIndex == 1;
49443     },
49444
49445     getRenderer : function(col){
49446         return col == 1 ?
49447             this.renderCellDelegate : this.renderPropDelegate;
49448     },
49449
49450     renderProp : function(v){
49451         return this.getPropertyName(v);
49452     },
49453
49454     renderCell : function(val){
49455         var rv = val;
49456         if(val instanceof Date){
49457             rv = this.renderDate(val);
49458         }else if(typeof val == 'boolean'){
49459             rv = this.renderBool(val);
49460         }
49461         return Roo.util.Format.htmlEncode(rv);
49462     },
49463
49464     getPropertyName : function(name){
49465         var pn = this.grid.propertyNames;
49466         return pn && pn[name] ? pn[name] : name;
49467     },
49468
49469     getCellEditor : function(colIndex, rowIndex){
49470         var p = this.store.getProperty(rowIndex);
49471         var n = p.data['name'], val = p.data['value'];
49472         
49473         if(typeof(this.grid.customEditors[n]) == 'string'){
49474             return this.editors[this.grid.customEditors[n]];
49475         }
49476         if(typeof(this.grid.customEditors[n]) != 'undefined'){
49477             return this.grid.customEditors[n];
49478         }
49479         if(val instanceof Date){
49480             return this.editors['date'];
49481         }else if(typeof val == 'number'){
49482             return this.editors['number'];
49483         }else if(typeof val == 'boolean'){
49484             return this.editors['boolean'];
49485         }else{
49486             return this.editors['string'];
49487         }
49488     }
49489 });
49490
49491 /**
49492  * @class Roo.grid.PropertyGrid
49493  * @extends Roo.grid.EditorGrid
49494  * This class represents the  interface of a component based property grid control.
49495  * <br><br>Usage:<pre><code>
49496  var grid = new Roo.grid.PropertyGrid("my-container-id", {
49497       
49498  });
49499  // set any options
49500  grid.render();
49501  * </code></pre>
49502   
49503  * @constructor
49504  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49505  * The container MUST have some type of size defined for the grid to fill. The container will be
49506  * automatically set to position relative if it isn't already.
49507  * @param {Object} config A config object that sets properties on this grid.
49508  */
49509 Roo.grid.PropertyGrid = function(container, config){
49510     config = config || {};
49511     var store = new Roo.grid.PropertyStore(this);
49512     this.store = store;
49513     var cm = new Roo.grid.PropertyColumnModel(this, store);
49514     store.store.sort('name', 'ASC');
49515     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
49516         ds: store.store,
49517         cm: cm,
49518         enableColLock:false,
49519         enableColumnMove:false,
49520         stripeRows:false,
49521         trackMouseOver: false,
49522         clicksToEdit:1
49523     }, config));
49524     this.getGridEl().addClass('x-props-grid');
49525     this.lastEditRow = null;
49526     this.on('columnresize', this.onColumnResize, this);
49527     this.addEvents({
49528          /**
49529              * @event beforepropertychange
49530              * Fires before a property changes (return false to stop?)
49531              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49532              * @param {String} id Record Id
49533              * @param {String} newval New Value
49534          * @param {String} oldval Old Value
49535              */
49536         "beforepropertychange": true,
49537         /**
49538              * @event propertychange
49539              * Fires after a property changes
49540              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49541              * @param {String} id Record Id
49542              * @param {String} newval New Value
49543          * @param {String} oldval Old Value
49544              */
49545         "propertychange": true
49546     });
49547     this.customEditors = this.customEditors || {};
49548 };
49549 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
49550     
49551      /**
49552      * @cfg {Object} customEditors map of colnames=> custom editors.
49553      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
49554      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
49555      * false disables editing of the field.
49556          */
49557     
49558       /**
49559      * @cfg {Object} propertyNames map of property Names to their displayed value
49560          */
49561     
49562     render : function(){
49563         Roo.grid.PropertyGrid.superclass.render.call(this);
49564         this.autoSize.defer(100, this);
49565     },
49566
49567     autoSize : function(){
49568         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
49569         if(this.view){
49570             this.view.fitColumns();
49571         }
49572     },
49573
49574     onColumnResize : function(){
49575         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
49576         this.autoSize();
49577     },
49578     /**
49579      * Sets the data for the Grid
49580      * accepts a Key => Value object of all the elements avaiable.
49581      * @param {Object} data  to appear in grid.
49582      */
49583     setSource : function(source){
49584         this.store.setSource(source);
49585         //this.autoSize();
49586     },
49587     /**
49588      * Gets all the data from the grid.
49589      * @return {Object} data  data stored in grid
49590      */
49591     getSource : function(){
49592         return this.store.getSource();
49593     }
49594 });/*
49595  * Based on:
49596  * Ext JS Library 1.1.1
49597  * Copyright(c) 2006-2007, Ext JS, LLC.
49598  *
49599  * Originally Released Under LGPL - original licence link has changed is not relivant.
49600  *
49601  * Fork - LGPL
49602  * <script type="text/javascript">
49603  */
49604  
49605 /**
49606  * @class Roo.LoadMask
49607  * A simple utility class for generically masking elements while loading data.  If the element being masked has
49608  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
49609  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
49610  * element's UpdateManager load indicator and will be destroyed after the initial load.
49611  * @constructor
49612  * Create a new LoadMask
49613  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
49614  * @param {Object} config The config object
49615  */
49616 Roo.LoadMask = function(el, config){
49617     this.el = Roo.get(el);
49618     Roo.apply(this, config);
49619     if(this.store){
49620         this.store.on('beforeload', this.onBeforeLoad, this);
49621         this.store.on('load', this.onLoad, this);
49622         this.store.on('loadexception', this.onLoad, this);
49623         this.removeMask = false;
49624     }else{
49625         var um = this.el.getUpdateManager();
49626         um.showLoadIndicator = false; // disable the default indicator
49627         um.on('beforeupdate', this.onBeforeLoad, this);
49628         um.on('update', this.onLoad, this);
49629         um.on('failure', this.onLoad, this);
49630         this.removeMask = true;
49631     }
49632 };
49633
49634 Roo.LoadMask.prototype = {
49635     /**
49636      * @cfg {Boolean} removeMask
49637      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
49638      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
49639      */
49640     /**
49641      * @cfg {String} msg
49642      * The text to display in a centered loading message box (defaults to 'Loading...')
49643      */
49644     msg : 'Loading...',
49645     /**
49646      * @cfg {String} msgCls
49647      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
49648      */
49649     msgCls : 'x-mask-loading',
49650
49651     /**
49652      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
49653      * @type Boolean
49654      */
49655     disabled: false,
49656
49657     /**
49658      * Disables the mask to prevent it from being displayed
49659      */
49660     disable : function(){
49661        this.disabled = true;
49662     },
49663
49664     /**
49665      * Enables the mask so that it can be displayed
49666      */
49667     enable : function(){
49668         this.disabled = false;
49669     },
49670
49671     // private
49672     onLoad : function(){
49673         this.el.unmask(this.removeMask);
49674     },
49675
49676     // private
49677     onBeforeLoad : function(){
49678         if(!this.disabled){
49679             this.el.mask(this.msg, this.msgCls);
49680         }
49681     },
49682
49683     // private
49684     destroy : function(){
49685         if(this.store){
49686             this.store.un('beforeload', this.onBeforeLoad, this);
49687             this.store.un('load', this.onLoad, this);
49688             this.store.un('loadexception', this.onLoad, this);
49689         }else{
49690             var um = this.el.getUpdateManager();
49691             um.un('beforeupdate', this.onBeforeLoad, this);
49692             um.un('update', this.onLoad, this);
49693             um.un('failure', this.onLoad, this);
49694         }
49695     }
49696 };/*
49697  * Based on:
49698  * Ext JS Library 1.1.1
49699  * Copyright(c) 2006-2007, Ext JS, LLC.
49700  *
49701  * Originally Released Under LGPL - original licence link has changed is not relivant.
49702  *
49703  * Fork - LGPL
49704  * <script type="text/javascript">
49705  */
49706 Roo.XTemplate = function(){
49707     Roo.XTemplate.superclass.constructor.apply(this, arguments);
49708     var s = this.html;
49709
49710     s = ['<tpl>', s, '</tpl>'].join('');
49711
49712     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
49713
49714     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
49715     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
49716     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
49717     var m, id = 0;
49718     var tpls = [];
49719
49720     while(m = s.match(re)){
49721        var m2 = m[0].match(nameRe);
49722        var m3 = m[0].match(ifRe);
49723        var m4 = m[0].match(execRe);
49724        var exp = null, fn = null, exec = null;
49725        var name = m2 && m2[1] ? m2[1] : '';
49726        if(m3){
49727            exp = m3 && m3[1] ? m3[1] : null;
49728            if(exp){
49729                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
49730            }
49731        }
49732        if(m4){
49733            exp = m4 && m4[1] ? m4[1] : null;
49734            if(exp){
49735                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
49736            }
49737        }
49738        if(name){
49739            switch(name){
49740                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
49741                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
49742                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
49743            }
49744        }
49745        tpls.push({
49746             id: id,
49747             target: name,
49748             exec: exec,
49749             test: fn,
49750             body: m[1]||''
49751         });
49752        s = s.replace(m[0], '{xtpl'+ id + '}');
49753        ++id;
49754     }
49755     for(var i = tpls.length-1; i >= 0; --i){
49756         this.compileTpl(tpls[i]);
49757     }
49758     this.master = tpls[tpls.length-1];
49759     this.tpls = tpls;
49760 };
49761 Roo.extend(Roo.XTemplate, Roo.Template, {
49762
49763     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
49764
49765     applySubTemplate : function(id, values, parent){
49766         var t = this.tpls[id];
49767         if(t.test && !t.test.call(this, values, parent)){
49768             return '';
49769         }
49770         if(t.exec && t.exec.call(this, values, parent)){
49771             return '';
49772         }
49773         var vs = t.target ? t.target.call(this, values, parent) : values;
49774         parent = t.target ? values : parent;
49775         if(t.target && vs instanceof Array){
49776             var buf = [];
49777             for(var i = 0, len = vs.length; i < len; i++){
49778                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
49779             }
49780             return buf.join('');
49781         }
49782         return t.compiled.call(this, vs, parent);
49783     },
49784
49785     compileTpl : function(tpl){
49786         var fm = Roo.util.Format;
49787         var useF = this.disableFormats !== true;
49788         var sep = Roo.isGecko ? "+" : ",";
49789         var fn = function(m, name, format, args){
49790             if(name.substr(0, 4) == 'xtpl'){
49791                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
49792             }
49793             var v;
49794             if(name.indexOf('.') != -1){
49795                 v = name;
49796             }else{
49797                 v = "values['" + name + "']";
49798             }
49799             if(format && useF){
49800                 args = args ? ',' + args : "";
49801                 if(format.substr(0, 5) != "this."){
49802                     format = "fm." + format + '(';
49803                 }else{
49804                     format = 'this.call("'+ format.substr(5) + '", ';
49805                     args = ", values";
49806                 }
49807             }else{
49808                 args= ''; format = "("+v+" === undefined ? '' : ";
49809             }
49810             return "'"+ sep + format + v + args + ")"+sep+"'";
49811         };
49812         var body;
49813         // branched to use + in gecko and [].join() in others
49814         if(Roo.isGecko){
49815             body = "tpl.compiled = function(values, parent){ return '" +
49816                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
49817                     "';};";
49818         }else{
49819             body = ["tpl.compiled = function(values, parent){ return ['"];
49820             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
49821             body.push("'].join('');};");
49822             body = body.join('');
49823         }
49824         /** eval:var:zzzzzzz */
49825         eval(body);
49826         return this;
49827     },
49828
49829     applyTemplate : function(values){
49830         return this.master.compiled.call(this, values, {});
49831         var s = this.subs;
49832     },
49833
49834     apply : function(){
49835         return this.applyTemplate.apply(this, arguments);
49836     },
49837
49838     compile : function(){return this;}
49839 });
49840
49841 Roo.XTemplate.from = function(el){
49842     el = Roo.getDom(el);
49843     return new Roo.XTemplate(el.value || el.innerHTML);
49844 };/*
49845  * Original code for Roojs - LGPL
49846  * <script type="text/javascript">
49847  */
49848  
49849 /**
49850  * @class Roo.XComponent
49851  * A delayed Element creator...
49852  * 
49853  * Mypart.xyx = new Roo.XComponent({
49854
49855     parent : 'Mypart.xyz', // empty == document.element.!!
49856     order : '001',
49857     name : 'xxxx'
49858     region : 'xxxx'
49859     disabled : function() {} 
49860      
49861     tree : function() { // return an tree of xtype declared components
49862         var MODULE = this;
49863         return 
49864         {
49865             xtype : 'NestedLayoutPanel',
49866             // technicall
49867         }
49868      ]
49869  *})
49870  * @extends Roo.util.Observable
49871  * @constructor
49872  * @param cfg {Object} configuration of component
49873  * 
49874  */
49875 Roo.XComponent = function(cfg) {
49876     Roo.apply(this, cfg);
49877     this.addEvents({ 
49878         /**
49879              * @event built
49880              * Fires when this the componnt is built
49881              * @param {Roo.XComponent} c the component
49882              */
49883         'built' : true,
49884         /**
49885              * @event buildcomplete
49886              * Fires on the top level element when all elements have been built
49887              * @param {Roo.XComponent} c the top level component.
49888          */
49889         'buildcomplete' : true,
49890         
49891     });
49892     
49893     Roo.XComponent.register(this);
49894     this.modules = false;
49895     this.el = false; // where the layout goes..
49896     
49897     
49898 }
49899 Roo.extend(Roo.XComponent, Roo.util.Observable, {
49900     /**
49901      * @property el
49902      * The created element (with Roo.factory())
49903      * @type {Roo.Layout}
49904      */
49905     el  : false,
49906     
49907     /**
49908      * @property el
49909      * for BC  - use el in new code
49910      * @type {Roo.Layout}
49911      */
49912     panel : false,
49913     
49914     /**
49915      * @property layout
49916      * for BC  - use el in new code
49917      * @type {Roo.Layout}
49918      */
49919     layout : false,
49920     
49921      /**
49922      * @cfg {Function|boolean} disabled
49923      * If this module is disabled by some rule, return true from the funtion
49924      */
49925     disabled : false,
49926     
49927     /**
49928      * @cfg {String} parent 
49929      * Name of parent element which it get xtype added to..
49930      */
49931     parent: false,
49932     
49933     /**
49934      * @cfg {String} order
49935      * Used to set the order in which elements are created (usefull for multiple tabs)
49936      */
49937     
49938     order : false,
49939     /**
49940      * @cfg {String} name
49941      * String to display while loading.
49942      */
49943     name : false,
49944     /**
49945      * @cfg {Array} items
49946      * A single item array - the first element is the root of the tree..
49947      * It's done this way to stay compatible with the Xtype system...
49948      */
49949     items : false,
49950      
49951      
49952     
49953 });
49954
49955 Roo.apply(Roo.XComponent, {
49956     
49957     /**
49958      * @property  buildCompleted
49959      * True when the builder has completed building the interface.
49960      * @type Boolean
49961      */
49962     buildCompleted : false,
49963      
49964     /**
49965      * @property  topModule
49966      * the upper most module - uses document.element as it's constructor.
49967      * @type Object
49968      */
49969      
49970     topModule  : false,
49971       
49972     /**
49973      * @property  modules
49974      * array of modules to be created by registration system.
49975      * @type Roo.XComponent
49976      */
49977     
49978     modules : [],
49979       
49980     
49981     /**
49982      * Register components to be built later.
49983      *
49984      * This solves the following issues
49985      * - Building is not done on page load, but after an authentication process has occured.
49986      * - Interface elements are registered on page load
49987      * - Parent Interface elements may not be loaded before child, so this handles that..
49988      * 
49989      *
49990      * example:
49991      * 
49992      * MyApp.register({
49993           order : '000001',
49994           module : 'Pman.Tab.projectMgr',
49995           region : 'center',
49996           parent : 'Pman.layout',
49997           disabled : false,  // or use a function..
49998         })
49999      
50000      * * @param {Object} details about module
50001      */
50002     register : function(obj) {
50003         this.modules.push(obj);
50004          
50005     },
50006     /**
50007      * convert a string to an object..
50008      * 
50009      */
50010     
50011     toObject : function(str)
50012     {
50013         if (!str || typeof(str) == 'object') {
50014             return str;
50015         }
50016         var ar = str.split('.');
50017         var rt, o;
50018         rt = ar.shift();
50019             /** eval:var:o */
50020         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
50021         if (o === false) {
50022             throw "Module not found : " + str;
50023         }
50024         Roo.each(ar, function(e) {
50025             if (typeof(o[e]) == 'undefined') {
50026                 throw "Module not found : " + str;
50027             }
50028             o = o[e];
50029         });
50030         return o;
50031         
50032     },
50033     
50034     
50035     /**
50036      * move modules into their correct place in the tree..
50037      * 
50038      */
50039     preBuild : function ()
50040     {
50041         
50042         Roo.each(this.modules , function (obj)
50043         {
50044             obj.parent = this.toObject(obj.parent);
50045             
50046             if (!obj.parent) {
50047                 this.topModule = obj;
50048                 return;
50049             }
50050             
50051             if (!obj.parent.modules) {
50052                 obj.parent.modules = new Roo.util.MixedCollection(false, 
50053                     function(o) { return o.order + '' }
50054                 );
50055             }
50056             
50057             obj.parent.modules.add(obj);
50058         }, this);
50059     },
50060     
50061      /**
50062      * make a list of modules to build.
50063      * @return {Array} list of modules. 
50064      */ 
50065     
50066     buildOrder : function()
50067     {
50068         var _this = this;
50069         var cmp = function(a,b) {   
50070             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
50071         };
50072         
50073         if (!this.topModule || !this.topModule.modules) {
50074             throw "No top level modules to build";
50075         }
50076        
50077         // make a flat list in order of modules to build.
50078         var mods = [ this.topModule ];
50079         
50080         
50081         // add modules to their parents..
50082         var addMod = function(m) {
50083            // console.log(m.modKey);
50084             
50085             mods.push(m);
50086             if (m.modules) {
50087                 m.modules.keySort('ASC',  cmp );
50088                 m.modules.each(addMod);
50089             }
50090             // not sure if this is used any more..
50091             if (m.finalize) {
50092                 m.finalize.name = m.name + " (clean up) ";
50093                 mods.push(m.finalize);
50094             }
50095             
50096         }
50097         this.topModule.modules.keySort('ASC',  cmp );
50098         this.topModule.modules.each(addMod);
50099         return mods;
50100     },
50101     
50102      /**
50103      * Build the registered modules.
50104      * @param {Object} parent element.
50105      * @param {Function} optional method to call after module has been added.
50106      * 
50107      */ 
50108    
50109     build : function() 
50110     {
50111         
50112         this.preBuild();
50113         var mods = this.buildOrder();
50114       
50115         //this.allmods = mods;
50116         //console.log(mods);
50117         //return;
50118         if (!mods.length) { // should not happen
50119             throw "NO modules!!!";
50120         }
50121         
50122         
50123         
50124         // flash it up as modal - so we store the mask!?
50125         Roo.MessageBox.show({ title: 'loading' });
50126         Roo.MessageBox.show({
50127            title: "Please wait...",
50128            msg: "Building Interface...",
50129            width:450,
50130            progress:true,
50131            closable:false,
50132            modal: false
50133           
50134         });
50135         var total = mods.length;
50136         
50137         var _this = this;
50138         var progressRun = function() {
50139             if (!mods.length) {
50140                 console.log('hide?');
50141                 Roo.MessageBox.hide();
50142                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
50143                 return;    
50144             }
50145             
50146             var m = mods.shift();
50147             console.log(m);
50148             if (typeof(m) == 'function') { // not sure if this is supported any more..
50149                 m.call(this);
50150                 return progressRun.defer(10, _this);
50151             } 
50152             
50153             Roo.MessageBox.updateProgress(
50154                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
50155                     " of " + total + 
50156                     (m.name ? (' - ' + m.name) : '')
50157                     );
50158             
50159          
50160             
50161             var disabled = (typeof(m.disabled) == 'function') ?
50162                 m.disabled.call(m.module.disabled) : m.disabled;    
50163             
50164             
50165             if (disabled) {
50166                 return progressRun(); // we do not update the display!
50167             }
50168             
50169             if (!m.parent) {
50170                 // it's a top level one..
50171                 var layoutbase = new Ext.BorderLayout(document.body, {
50172                
50173                     center: {
50174                          titlebar: false,
50175                          autoScroll:false,
50176                          closeOnTab: true,
50177                          tabPosition: 'top',
50178                          //resizeTabs: true,
50179                          alwaysShowTabs: true,
50180                          minTabWidth: 140
50181                     }
50182                 });
50183                 var tree = m.tree();
50184                 tree.region = 'center';
50185                 m.el = layoutbase.addxtype(tree);
50186                 m.panel = m.el;
50187                 m.layout = m.panel.layout;    
50188                 return progressRun.defer(10, _this);
50189             }
50190             
50191             var tree = m.tree();
50192             tree.region = tree.region || m.region;
50193             m.el = m.parent.el.addxtype(tree);
50194             m.fireEvent('built', m);
50195             m.panel = m.el;
50196             m.layout = m.panel.layout;    
50197             progressRun.defer(10, _this); 
50198             
50199         }
50200         progressRun.defer(1, _this);
50201      
50202         
50203         
50204     }
50205      
50206    
50207     
50208     
50209 });
50210  //<script type="text/javascript">
50211
50212
50213 /**
50214  * @class Roo.Login
50215  * @extends Roo.LayoutDialog
50216  * A generic Login Dialog..... - only one needed in theory!?!?
50217  *
50218  * Fires XComponent builder on success...
50219  * 
50220  * Sends 
50221  *    username,password, lang = for login actions.
50222  *    check = 1 for periodic checking that sesion is valid.
50223  *    passwordRequest = email request password
50224  *    logout = 1 = to logout
50225  * 
50226  * Affects: (this id="????" elements)
50227  *   loading  (removed) (used to indicate application is loading)
50228  *   loading-mask (hides) (used to hide application when it's building loading)
50229  *   
50230  * 
50231  * Usage: 
50232  *    
50233  * 
50234  * Myapp.login = Roo.Login({
50235      url: xxxx,
50236    
50237      realm : 'Myapp', 
50238      
50239      
50240      method : 'POST',
50241      
50242      
50243      * 
50244  })
50245  * 
50246  * 
50247  * 
50248  **/
50249  
50250 Roo.Login = function(cfg)
50251 {
50252     this.addEvents({
50253         'refreshed' : true,
50254     });
50255     
50256     Roo.apply(this,cfg);
50257     
50258     Roo.onReady(function() {
50259         this.onLoad();
50260     }, this);
50261     // call parent..
50262     
50263    
50264     Roo.Login.superclass.constructor.call(this, this);
50265     //this.addxtype(this.items[0]);
50266     
50267     
50268 }
50269
50270
50271 Roo.extend(Roo.Login, Roo.LayoutDialog, {
50272     
50273     /**
50274      * @cfg {String} method
50275      * Method used to query for login details.
50276      */
50277     
50278     method : 'POST',
50279     /**
50280      * @cfg {String} url
50281      * URL to query login data. - eg. baseURL + '/Login.php'
50282      */
50283     url : '',
50284     
50285     /**
50286      * @property user
50287      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
50288      * @type {Object} 
50289      */
50290     user : false,
50291     /**
50292      * @property checkFails
50293      * Number of times we have attempted to get authentication check, and failed.
50294      * @type {Number} 
50295      */
50296     checkFails : 0,
50297       /**
50298      * @property intervalID
50299      * The window interval that does the constant login checking.
50300      * @type {Number} 
50301      */
50302     intervalID : 0,
50303     
50304     
50305     onLoad : function() // called on page load...
50306     {
50307         // load 
50308          
50309         if (Roo.get('loading')) { // clear any loading indicator..
50310             Roo.get('loading').remove();
50311         }
50312         
50313         //this.switchLang('en'); // set the language to english..
50314        
50315         this.check({
50316             success:  function(response, opts)  {  // check successfull...
50317             
50318                 var res = this.processResponse(response);
50319                 this.checkFails =0;
50320                 if (!res.success) { // error!
50321                     this.checkFails = 5;
50322                     //console.log('call failure');
50323                     return this.failure(response,opts);
50324                 }
50325                 
50326                 if (!res.data.id) { // id=0 == login failure.
50327                     return this.show();
50328                 }
50329                 
50330                               
50331                         //console.log(success);
50332                 this.fillAuth(res.data);   
50333                 this.checkFails =0;
50334                 Roo.XComponent.build();
50335             },
50336             failure : this.show
50337         });
50338         
50339     }, 
50340     
50341     
50342     check: function(cfg) // called every so often to refresh cookie etc..
50343     {
50344         if (cfg.again) { // could be undefined..
50345             this.checkFails++;
50346         } else {
50347             this.checkFails = 0;
50348         }
50349         var _this = this;
50350         if (this.sending) {
50351             if ( this.checkFails > 4) {
50352                 Roo.MessageBox.alert("Error",  
50353                     "Error getting authentication status. - try reloading, or wait a while", function() {
50354                         _this.sending = false;
50355                     }); 
50356                 return;
50357             }
50358             cfg.again = true;
50359             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
50360             return;
50361         }
50362         this.sending = true;
50363         
50364         Roo.Ajax.request({  
50365             url: this.url,
50366             params: {
50367                 getAuthUser: true
50368             },  
50369             method: this.method,
50370             success:  cfg.success || this.success,
50371             failure : cfg.failure || this.failure,
50372             scope : this,
50373             callCfg : cfg
50374               
50375         });  
50376     }, 
50377     
50378     
50379     logout: function()
50380     {
50381         window.onbeforeunload = function() { }; // false does not work for IE..
50382         this.user = false;
50383         var _this = this;
50384         
50385         Roo.Ajax.request({  
50386             url: this.url,
50387             params: {
50388                 logout: 1
50389             },  
50390             method: 'GET',
50391             failure : function() {
50392                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
50393                     document.location = document.location.toString() + '?ts=' + Math.random();
50394                 });
50395                 
50396             },
50397             success : function() {
50398                 _this.user = false;
50399                 this.checkFails =0;
50400                 // fixme..
50401                 document.location = document.location.toString() + '?ts=' + Math.random();
50402             }
50403               
50404               
50405         }); 
50406     },
50407     
50408     processResponse : function (response)
50409     {
50410         var res = '';
50411         try {
50412             res = Roo.decode(response.responseText);
50413             // oops...
50414             if (typeof(res) != 'object') {
50415                 res = { success : false, errorMsg : res, errors : true };
50416             }
50417             if (typeof(res.success) == 'undefined') {
50418                 res.success = false;
50419             }
50420             
50421         } catch(e) {
50422             res = { success : false,  errorMsg : response.responseText, errors : true };
50423         }
50424         return res;
50425     },
50426     
50427     success : function(response, opts)  // check successfull...
50428     {  
50429         this.sending = false;
50430         var res = this.processResponse(response);
50431         if (!res.success) {
50432             return this.failure(response, opts);
50433         }
50434         if (!res.data || !res.data.id) {
50435             return this.failure(response,opts);
50436         }
50437         //console.log(res);
50438         this.fillAuth(res.data);
50439         
50440         this.checkFails =0;
50441         
50442     },
50443     
50444     
50445     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
50446     {
50447         this.authUser = -1;
50448         this.sending = false;
50449         var res = this.processResponse(response);
50450         //console.log(res);
50451         if ( this.checkFails > 2) {
50452         
50453             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
50454                 "Error getting authentication status. - try reloading"); 
50455             return;
50456         }
50457         opts.callCfg.again = true;
50458         this.check.defer(1000, this, [ opts.callCfg ]);
50459         return;  
50460     },
50461     
50462     
50463     
50464     fillAuth: function(au) {
50465         this.startAuthCheck();
50466         this.authUserId = au.id;
50467         this.authUser = au;
50468         this.lastChecked = new Date();
50469         this.fireEvent('refreshed', au);
50470         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
50471         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
50472         au.lang = au.lang || 'en';
50473         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
50474         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
50475         this.switchLang(au.lang );
50476         
50477      
50478         // open system... - -on setyp..
50479         if (this.authUserId  < 0) {
50480             Roo.MessageBox.alert("Warning", 
50481                 "This is an open system - please set up a admin user with a password.");  
50482         }
50483          
50484         //Pman.onload(); // which should do nothing if it's a re-auth result...
50485         
50486              
50487     },
50488     
50489     startAuthCheck : function() // starter for timeout checking..
50490     {
50491         if (this.intervalID) { // timer already in place...
50492             return false;
50493         }
50494         var _this = this;
50495         this.intervalID =  window.setInterval(function() {
50496               _this.check(false);
50497             }, 120000); // every 120 secs = 2mins..
50498         
50499         
50500     },
50501          
50502     
50503     switchLang : function (lang) 
50504     {
50505         _T = typeof(_T) == 'undefined' ? false : _T;
50506           if (!_T || !lang.length) {
50507             return;
50508         }
50509         
50510         if (!_T && lang != 'en') {
50511             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50512             return;
50513         }
50514         
50515         if (typeof(_T.en) == 'undefined') {
50516             _T.en = {};
50517             Roo.apply(_T.en, _T);
50518         }
50519         
50520         if (typeof(_T[lang]) == 'undefined') {
50521             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50522             return;
50523         }
50524         
50525         
50526         Roo.apply(_T, _T[lang]);
50527         // just need to set the text values for everything...
50528         var _this = this;
50529         /* this will not work ...
50530         if (this.form) { 
50531             
50532                
50533             function formLabel(name, val) {
50534                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
50535             }
50536             
50537             formLabel('password', "Password"+':');
50538             formLabel('username', "Email Address"+':');
50539             formLabel('lang', "Language"+':');
50540             this.dialog.setTitle("Login");
50541             this.dialog.buttons[0].setText("Forgot Password");
50542             this.dialog.buttons[1].setText("Login");
50543         }
50544         */
50545         
50546         
50547     },
50548     
50549     
50550     title: "Login",
50551     modal: true,
50552     width:  350,
50553     //height: 230,
50554     height: 180,
50555     shadow: true,
50556     minWidth:200,
50557     minHeight:180,
50558     //proxyDrag: true,
50559     closable: false,
50560     draggable: false,
50561     collapsible: false,
50562     resizable: false,
50563     center: {  // needed??
50564         autoScroll:false,
50565         titlebar: false,
50566        // tabPosition: 'top',
50567         hideTabs: true,
50568         closeOnTab: true,
50569         alwaysShowTabs: false
50570     } ,
50571     listeners : {
50572         
50573         show  : function(dlg)
50574         {
50575             //console.log(this);
50576             this.form = this.layout.getRegion('center').activePanel.form;
50577             this.form.dialog = dlg;
50578             this.buttons[0].form = this.form;
50579             this.buttons[0].dialog = dlg
50580             this.buttons[1].form = this.form;
50581             this.buttons[1].dialog = dlg;
50582            
50583            //this.resizeToLogo.defer(1000,this);
50584             // this is all related to resizing for logos..
50585             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
50586            //// if (!sz) {
50587              //   this.resizeToLogo.defer(1000,this);
50588              //   return;
50589            // }
50590             //var w = Ext.lib.Dom.getViewWidth() - 100;
50591             //var h = Ext.lib.Dom.getViewHeight() - 100;
50592             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
50593             //this.center();
50594             if (this.disabled) {
50595                 this.hide();
50596                 return;
50597             }
50598             
50599             if (this.user.id < 0) { // used for inital setup situations.
50600                 return;
50601             }
50602             
50603             if (this.intervalID) {
50604                 // remove the timer
50605                 window.clearInterval(this.intervalID);
50606                 this.intervalID = false;
50607             }
50608             
50609             
50610             if (Roo.get('loading')) {
50611                 Roo.get('loading').remove();
50612             }
50613             if (Roo.get('loading-mask')) {
50614                 Roo.get('loading-mask').hide();
50615             }
50616             
50617             //incomming._node = tnode;
50618             this.form.reset();
50619             //this.dialog.modal = !modal;
50620             //this.dialog.show();
50621             this.el.unmask(); 
50622             
50623             
50624             this.form.setValues({
50625                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
50626                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
50627             });
50628             
50629             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
50630             if (this.form.findField('username').getValue().length > 0 ){
50631                 this.form.findField('password').focus();
50632             } else {
50633                this.form.findField('username').focus();
50634             }
50635     
50636         }
50637     },
50638     items : [
50639          {
50640        
50641             xtype : 'ContentPanel',
50642             xns : Roo,
50643             region: 'center',
50644             fitToFrame : true,
50645             
50646             items : [
50647     
50648                 {
50649                
50650                     xtype : 'Form',
50651                     xns : Roo.form,
50652                     labelWidth: 100,
50653                     style : 'margin: 10px;',
50654                     
50655                     listeners : {
50656                         actionfailed : function(f, act) {
50657                             // form can return { errors: .... }
50658                                 
50659                             //act.result.errors // invalid form element list...
50660                             //act.result.errorMsg// invalid form element list...
50661                             
50662                             this.dialog.el.unmask();
50663                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
50664                                         "Login failed - communication error - try again.");
50665                                       
50666                         },
50667                         actioncomplete: function(re, act) {
50668                              
50669                             Roo.state.Manager.set(
50670                                 this.dialog.realm + '.username',  
50671                                     this.findField('username').getValue()
50672                             );
50673                             Roo.state.Manager.set(
50674                                 this.dialog.realm + '.lang',  
50675                                 this.findField('lang').getValue() 
50676                             );
50677                             
50678                             this.dialog.fillAuth(act.result.data);
50679                               
50680                             this.dialog.hide();
50681                             
50682                             if (Roo.get('loading-mask')) {
50683                                 Roo.get('loading-mask').show();
50684                             }
50685                             Roo.XComponent.build();
50686                             
50687                              
50688                             
50689                         }
50690                     },
50691                     items : [
50692                         {
50693                             xtype : 'TextField',
50694                             xns : Roo.form,
50695                             fieldLabel: "Email Address",
50696                             name: 'username',
50697                             width:200,
50698                             autoCreate : {tag: "input", type: "text", size: "20"}
50699                         },
50700                         {
50701                             xtype : 'TextField',
50702                             xns : Roo.form,
50703                             fieldLabel: "Password",
50704                             inputType: 'password',
50705                             name: 'password',
50706                             width:200,
50707                             autoCreate : {tag: "input", type: "text", size: "20"},
50708                             listeners : {
50709                                 specialkey : function(e,ev) {
50710                                     if (ev.keyCode == 13) {
50711                                         this.form.dialog.el.mask("Logging in");
50712                                         this.form.doAction('submit', {
50713                                             url: this.form.dialog.url,
50714                                             method: this.form.dialog.method,
50715                                         });
50716                                     }
50717                                 }
50718                             }  
50719                         },
50720                         {
50721                             xtype : 'ComboBox',
50722                             xns : Roo.form,
50723                             fieldLabel: "Language",
50724                             name : 'langdisp',
50725                             store: {
50726                                 xtype : 'SimpleStore',
50727                                 fields: ['lang', 'ldisp'],
50728                                 data : [
50729                                     [ 'en', 'English' ],
50730                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
50731                                     [ 'zh_CN', '\u7C21\u4E2D' ]
50732                                 ]
50733                             },
50734                             
50735                             valueField : 'lang',
50736                             hiddenName:  'lang',
50737                             width: 200,
50738                             displayField:'ldisp',
50739                             typeAhead: false,
50740                             editable: false,
50741                             mode: 'local',
50742                             triggerAction: 'all',
50743                             emptyText:'Select a Language...',
50744                             selectOnFocus:true,
50745                             listeners : {
50746                                 select :  function(cb, rec, ix) {
50747                                     this.form.switchLang(rec.data.lang);
50748                                 }
50749                             }
50750                         
50751                         }
50752                     ]
50753                 }
50754                   
50755                 
50756             ]
50757         }
50758     ],
50759     buttons : [
50760         {
50761             xtype : 'Button',
50762             xns : 'Roo',
50763             text : "Forgot Password",
50764             listeners : {
50765                 click : function() {
50766                     //console.log(this);
50767                     var n = this.form.findField('username').getValue();
50768                     if (!n.length) {
50769                         Roo.MessageBox.alert("Error", "Fill in your email address");
50770                         return;
50771                     }
50772                     Roo.Ajax.request({
50773                         url: this.dialog.url,
50774                         params: {
50775                             passwordRequest: n
50776                         },
50777                         method: this.dialog.method,
50778                         success:  function(response, opts)  {  // check successfull...
50779                         
50780                             var res = this.dialog.processResponse(response);
50781                             if (!res.success) { // error!
50782                                Roo.MessageBox.alert("Error" ,
50783                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
50784                                return;
50785                             }
50786                             Roo.MessageBox.alert("Notice" ,
50787                                 "Please check you email for the Password Reset message");
50788                         },
50789                         failure : function() {
50790                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
50791                         }
50792                         
50793                     });
50794                 }
50795             }
50796         },
50797         {
50798             xtype : 'Button',
50799             xns : 'Roo',
50800             text : "Login",
50801             listeners : {
50802                 
50803                 click : function () {
50804                         
50805                     this.dialog.el.mask("Logging in");
50806                     this.form.doAction('submit', {
50807                             url: this.dialog.url,
50808                             method: this.dialog.method
50809                     });
50810                 }
50811             }
50812         }
50813     ]
50814   
50815   
50816 })
50817  
50818
50819
50820